BFPRT 演算法(TOP-K問題)

發表於2017-10-24

一:背景介紹

在一堆數中求其前k大或前k小的問題,簡稱TOP-K問題。而目前解決TOP-K問題最有效的演算法即是”BFPRT演算法”,又稱為”中位數的中位數演算法”,該演算法由Blum、Floyd、Pratt、Rivest、Tarjan提出,最壞時間複雜度為$O(n)$。

在首次接觸TOP-K問題時,我們的第一反應就是可以先對所有資料進行一次排序,然後取其前k即可,但是這麼做有兩個問題:

  • 快速排序的平均複雜度為$O(nlogn)$,但最壞時間複雜度為$O(n^2)$,不能始終保證較好的複雜度。
  • 我們只需要前k大的,而對其餘不需要的數也進行了排序,浪費了大量排序時間。

除這種方法之外,堆排序也是一個比較好的選擇,可以維護一個大小為k的堆,時間複雜度為$O(nlogk)$。

那是否還存在更有效的方法呢?受到快速排序的啟發,通過修改快速排序中主元的選取方法可以降低快速排序在最壞情況下的時間複雜度,並且我們的目的只是求出前k,故遞迴的規模變小,速度也隨之提高。下面來簡單回顧下快速排序的過程,以升序為例:

(1):選取主元(陣列中隨機一個元素);
(2):以選取的主元為分界點,把小於主元的放在左邊,大於主元的放在右邊;
(3):分別對左邊和右邊進行遞迴,重複上述過程。

二:BFPRT演算法過程及程式碼

BFPRT演算法步驟如下:

(1):選取主元;
(1.1):將n個元素劃分為$⌊frac n5⌋$個組,每組5個元素,若有剩餘,捨去;
(1.2):使用插入排序找到$⌊frac n5⌋$個組中每一組的中位數;
(1.3):對於(1.2)中找到的所有中位數,呼叫BFPRT演算法求出它們的中位數,作為主元;
(2):以(1.3)選取的主元為分界點,把小於主元的放在左邊,大於主元的放在右邊;
(3):判斷主元的位置與k的大小,有選擇的對左邊或右邊遞迴。

上面的描述可能並不易理解,先看下面這幅圖:

BFPRT()呼叫GetPivotIndex()和Partition()來求解第k小,在這過程中,GetPivotIndex()也呼叫了BFPRT(),即GetPivotIndex)和BFPRT()為互遞迴的關係。

下面為程式碼實現,其所求為前K小的數

執行如下:

三:時間複雜度分析

 

QQ截圖20171024111911

QQ截圖20171024112045

四:參考文獻

  • 演算法導論(第3版). Page 120.

相關文章