1. 程式人生 > >BFPRT演算法 線性時間選擇第k小元素

BFPRT演算法 線性時間選擇第k小元素

文章目錄

BFPRT演算法

1. 應用場景

線性時間內,從無序列表找到第k小的元素。

2. 基本思想

  • 首先把陣列按5個數為一組進行分組,最後不足5個的忽略。對每組數進行排序(如插入排序)求取其中位數。
  • 把上一步的所有中位數移到陣列的前面,對這些中位數遞迴呼叫BFPRT演算法求得他們的中位數。
  • 將上一步得到的中位數作為劃分的主元進行整個陣列的劃分。
  • 判斷第k個數在劃分結果的左邊、右邊還是恰好是劃分結果本身,前兩者遞迴處理,後者直接返回答案。

3. 演算法實現

void bubble_sort(int* a, int left, int right){
	int tmp;
	for(int i=left; i<right; i++){
		for(int j=right; j>i; j--){
			if(a[j] < a[j-1]) tmp = a[j], a[j] = a[j-1], a[j-1] = tmp;
		}
	}
} 

int partition(int* a, int left, int
right, int baseIdx){ int j = left - 1, tmp; //將基準放於陣列尾部 tmp = a[right], a[right] = a[baseIdx], a[baseIdx] = tmp; for(int i=left; i<right; i++){ if(a[i] <= a[right]) tmp = a[i], a[i] = a[++j], a[j] = tmp; } tmp = a[right], a[right] = a[++j], a[j] = tmp; return j; } int bfprt(int* a, int
left, int right, int k){ if(right - left +1 <= 5){ //小於等於5個數,直接排序得到結果 bubble_sort(a, left, right); return a[left + k -1]; } int t = left - 1, tmp; //t:當前替換到前面的中位數的下標 for(int st = left, ed; (ed=st+4) <= right; st += 5){ bubble_sort(a, st, ed); //將中位數替換到陣列前面,便於遞迴求取中位數的中位數 tmp = a[++t], a[t] = a[st+2], a[st+2] = tmp; } int baseIdx = (left + t) >> 1; //left到t的中位數的下標,作為主元的下標 bfprt(a, left, t, baseIdx-left + 1); //不關心中位數的值,保證中位數在正確的位置 int idx = partition(a, left, right, baseIdx), cur = idx - left + 1; if(k == cur) return a[idx]; else if(k < cur) return bfprt(a, left, idx-1, k); else return bfprt(a, idx+1, right, k-cur); }

4. 複雜度分析

劃分時以5個元素為一組求取中位數,共得到 n / 5 n/5 箇中位數,再遞迴求取中位數,複雜度為 T ( n / 5 ) T(n/5)

得到的中位數 x x 作為主元進行劃分,在 n / 5 n/5 箇中位數中,主元 x x 大於其中 1 / 2 n / 5 = n / 10 1/2*n/5=n/10 的中位數,而每個中位數在其本來的5個數的小組中又大於或等於其中的3個數,所以主元 x x 至少大於所有數中的 n / 10 3 = 3 / 10 n n/10*3=3/10*n 個。同理,主元 x x 至少小於所有數中的 3 / 10 n 3/10*n 個。即劃分之後,任意一邊的長度至少為 3 / 10 3/10 ,在最壞情況下,每次選擇都選到了 7 / 10 7/10 的那一部分,則遞迴的複雜度為 T ( 7 / 10 n ) T(7/10*n)

在每5個數求中位數和劃分的函式中,進行若干個次線性的掃描,其時間複雜度為 c n c*n ,其中 c c 為常數。其總的時間複雜度滿足 T ( n ) &lt; = T ( n / 5 ) + T ( 7 / 10 n ) + c n T(n) &lt;= T(n/5) + T(7/10*n) + c * n

我們假設 T ( n ) = x n T(n)=x*n ,其中 x x 不一定是常數(比如 x x 可以為 n n 的倍數,則對應的 T ( n ) = O ( n 2 ) ) T(n)=O(n^2)) 。則有 x n &lt; = x n / 5 + x 7 / 10 n + c n x*n &lt;= x*n/5 + x*7/10*n + c*n ,得到 x &lt; = 10 c x&lt;=10*c 。於是可以知道 x x n n 無關, T ( n ) &lt; = 10 c n T(n)&lt;=10*c*n ,為線性時間複雜度演算法。而這又是最壞情況下的分析,故BFPRT可以在最壞情況下以線性時間求得 n n 個數中的第 k k 個數。

5. References

https://www.cnblogs.com/informatics/p/5092741.html