期望為線性時間的選擇演算法
阿新 • • 發佈:2018-12-19
《演算法導論》第9章Randomized_Select演算法
Randomized_Select的原理與思想
從一個數組當中尋找第i小的元素,最簡單最暴力的方法就是將整個陣列按照升序進行排序操作,那麼第i個元素就是第i小的元素。
如果是以這種方式,那麼時間複雜度等同於排序時所使用的排序演算法,如果是快速排序,那麼此時時間複雜度為O(nlgn)。
那麼,有沒有一個演算法相對於整個陣列排序再查詢來的更快呢?比如線性時間O(n)?
Randomized_Select就是這樣一個基於隨機函式和快排引申出來的查詢函式。在期望線性時間內,可以找到任一順序統計量,特別是中位數。
- 第一步 通過隨機函式產生一個下標,這個下標大於等於左邊界,小於等於右邊界
int Randomized_Partition(vector<int>&p, int Left, int Right) { if (Left == Right) { return Left; } srand((unsigned int)time(0)); int key = (rand() % (Right - Left + 1)) + Left; for (int i = Left - 1, j = Left; j <= Right; j++) { if (p[j] < p[key]) { i++; swap(p[i], p[j]); if (i == key) { key = j; } } if (j == Right) { swap(p[key], p[i + 1]); key = i + 1; } } return key; }
- 隨機產生一個下標值,記為key,以key代表的值將整個陣列劃分為兩部分,這裡與快排一致。
- swap操作可能會改變key所代表的元素值,故在對key進行swap操作時需記錄新的key值
- 劃分完成之後返回key
int Randomized_Select(vector<int>&p, int Left, int Right, int i) { if (Left == Right) { return p[Left]; } int q = Randomized_Partition(p, Left, Right); int k = q - Left + 1; if (k==i) { return p[q]; } else if(i<k) { return Randomized_Select(p, Left, q - 1, i); } else { return Randomized_Select(p, q + 1, Right, i - k); } }
- Randomized_Select接受返回的key值,計算下標是否等於i
- 因為劃分保證了當前陣列中,key前面的元素都比key所代表的元素小,所以key所代表的元素是陣列中第key小的值
- 當key等於i時,說明所需值就是key,返回即可
- 如果i小於key,說明所需值在key的左部,需要進一步劃分
- 如果i大於key,說明所需值在key的右部,等同於在key的右部劃分中需要第i-key小的元素
總結
利用隨機數進行劃分,從數學角度上將最壞執行時間給平均掉了,等概率劃分的情況下使得最壞執行時間也不會太差。 書中還介紹了一種最壞情況為線性時間的選擇演算法,下次再寫。