在一大堆數中求其前k大或前k小的問題,簡稱top-k問題。而目前解決top-k問題最有效的演算法即是bfprt演算法,其又稱為中位數的中位數演算法,該演算法由blum、floyd、pratt、rivest、tarjan提出,最壞時間複雜度為o(n
)在首次接觸top-k問題時,我們的第一反應就是可以先對所有資料進行一次排序,然後取其前k即可,但是這麼做有兩個問題:
(1):快速排序的平均複雜度為o(n
logn
),但最壞時間複雜度為o(n
2),不能始終保證較好的複雜度。
(2):我們只需要前k大的,而對其餘不需要的數也進行了排序,浪費了大量排序時間。
除這種方法之外,堆排序也是乙個比較好的選擇,可以維護乙個大小為k的堆,時間複雜度為o(n
logk
)。那是否還存在更有效的方法呢?受到快速排序的啟發,通過修改快速排序中主元的選取方法可以降低快速排序在最壞情況下的時間複雜度(即bfprt演算法),並且我們的目的只是求出前k,故遞迴的規模變小,速度也隨之提高。下面來簡單回顧下快速排序的過程,以公升序為例:
(1):選取主元(首元素,尾元素或乙個隨機元素);
(2):以選取的主元為分界點,把小於主元的放在左邊,大於主元的放在右邊;
(3):分別對左邊和右邊進行遞迴,重複上述過程。
上面的描述可能並不易理解,先看下面這幅圖:
bfprt()呼叫getpivotindex()和partition()來求解第k小,在這過程中,getpivotindex()也呼叫了bfprt(),即getpivotindex)和bfprt()為互遞迴的關係。
下面為**實現,其所求為前k小的數:
/**
* bfprt演算法(前k小數問題)
** author 劉毅(limer)
* date 2017-01-25
* mode c++
*/#include
#include
using
namespace
std;
int insertsort(int
array, int left, int right); //插入排序,返回中位數下標
int getpivotindex(int
array, int left, int right); //返回中位數的中位數下標
int partition(int
array, int left, int right, int pivot_index); //利用中位數的中位數的下標進行劃分,返回分界線下標
int bfprt(int
array, int left, int right, const
int & k); //求第k小,返回其位置的下標
int main()
; cout
<< "原陣列:";
for (int i = 0; i < 10; i++)
cout
<< array[i] << " ";
cout
<< endl;
cout
<< "第"
<< k << "小值為:"
<< array[bfprt(array, 0, 9, k)] << endl;
cout
<< "變換後的陣列:";
for (int i = 0; i < 10; i++)
cout
<< array[i] << " ";
cout
<< endl;
return0;}
/* 插入排序,返回中位數下標 */
int insertsort(int
array, int left, int right)
return ((right - left) >> 1) + left;
}/* 返回中位數的中位數下標 */
int getpivotindex(int
array, int left, int right)
return bfprt(array, left, sub_right, ((sub_right - left + 1) >> 1) + 1);
}/* 利用中位數的中位數的下標進行劃分,返回分界線下標 */
int partition(int
array, int left, int right, int pivot_index)
swap(array[divide_index], array[right]); //最後把基準換回來
[1] 演算法導論(第3版)
[2] 演算法設計與分析基礎(第3版)
[3] wikipedia. median of medians
[4] acdreamers. bfprt 演算法
[5] noalgo. bfprt演算法
bfprt演算法解析
首先講一下bfprt演算法是幹嘛的?bfprt演算法是用來求陣列中第k小的元素的演算法,bfprt演算法可以在o n 時間內求出答案。對於求陣列中第k小的元素的問題,我們已經有很好的常規演算法了,這個演算法在最好的情況下時間複雜度是o n 但在最壞的情況下是o n 2 的,其實bfprt演算法就是在...
演算法 BFPRT(線性查詢演算法)
bfprt演算法解決的問題十分經典,即從某n個元素的序列中選出第k大 第k小 的元素,通過巧妙的分 析,bfprt可以保證在最壞情況下仍為線性時間複雜度。該演算法的思想與快速排序思想相似,當然,為使得演算法在最壞情況下,依然能達到o n 的時間複雜 度,五位演算法作者做了精妙的處理。演算法步驟 1 ...
BFPRT(線性查詢)演算法
bfprt演算法是解決從n個數中選擇第k大或第k小的數這個經典問題的著名演算法,但很多人並不了解其細節。本文將首先介紹求解這個第k小數字問題的幾個思路,然後重點介紹在最壞情況下複雜度仍然為o n 的bfprt演算法。關於選擇第k小的數字有許多方法,其效率和複雜度各不一樣,可以根據實際情況進行選擇。將...