BFPRT演算法 線性時間選擇第k小元素
阿新 • • 發佈:2019-01-09
文章目錄
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個元素為一組求取中位數,共得到 箇中位數,再遞迴求取中位數,複雜度為 。
得到的中位數 作為主元進行劃分,在 箇中位數中,主元 大於其中 的中位數,而每個中位數在其本來的5個數的小組中又大於或等於其中的3個數,所以主元 至少大於所有數中的 個。同理,主元 至少小於所有數中的 個。即劃分之後,任意一邊的長度至少為 ,在最壞情況下,每次選擇都選到了 的那一部分,則遞迴的複雜度為 。
在每5個數求中位數和劃分的函式中,進行若干個次線性的掃描,其時間複雜度為 ,其中 為常數。其總的時間複雜度滿足 。
我們假設 ,其中 不一定是常數(比如 可以為 的倍數,則對應的 。則有 ,得到 。於是可以知道 與 無關, ,為線性時間複雜度演算法。而這又是最壞情況下的分析,故BFPRT可以在最壞情況下以線性時間求得 個數中的第 個數。