1. 程式人生 > >從一個無序陣列中求出第K大/小的數

從一個無序陣列中求出第K大/小的數

這個題目可以作為練習寫大/小根堆的實現,不過貌似時間複雜度還是蠻高的。在洛谷上面一道模板題上面好像就超時了幾個點,不知道是不是我實現的問題。那麼除此之外,最容易想到的方法是先對該陣列進行排序,然後取出第K或MAX-K數來。當選擇使用快排的時候,時間複雜度是$O(nlogn)$。但還有一種更優的方法是利用快排劃分出來的主元位置再遞迴尋找,這種方法叫作隨機選擇演算法。它對任何輸入都可以達到$O(n)$的期望時間複雜度。

 

 1 #include <cstdlib>
 2 #include <algorithm>
 3 #include <cmath>
 4
5 //隨機選擇主元 其實也是快排的一種寫法 6 int randPartition(int A[],int left,int right) 7 { 8 // rand()/RAND_MAX得到一個0~1的小數再乘以相差值right-left最後加到left四捨五入得到隨機值 9 int p = round(1.0*rand()/RAND_MAX*(right-left)+ left); 10 swap(A[p],A[left]); 11 int temp = A[left]; 12 while(left<right) // 跳出迴圈時left==right
13 { 14 while(left < right && A[right]>temp) 15 right--; 16 A[left] = A[right]; 17 while(left < right && A[left]<=temp) 18 left++; 19 A[right] = A[left]; 20 } 21 A[left] = temp; 22 return left; 23 }
24 25 //遞迴實現查詢K值 26 int randSelect(int A[],int left,int right,int k) 27 { 28 if(left == right) 29 return A[left]; 30 int index = randPartition(A,left,right); 31 int M = index-left+1; // 表示從左到右第M個數 32 if(M==k) 33 return A[index]; 34 if(M<k) 35 randSelect(A,index+1,right,k-M); //注意 k-M 36 else if(M>k) 37 randSelect(A,left,index-1,k); 38 }
View Code