基於快速排序的O(N)時間複雜度的TopK算法原理

一、背景

       實際應用中,我們經常面臨這樣的問題,即從一個序列S中選擇其中最大的K個數(一般情況下K遠小於|S|),我們將這種問題稱爲TopK問題。

       舉一個例子,美劇《權利的遊戲》中的每個人物,觀衆都會對其進行選擇支持或不支持,這樣每個人物都會都應一個熱度值,這個值可以是支持者的數量,我們可以發現在騰訊視頻中該劇中熱度排名前3的人物加以了高亮標識,如下圖:

    諸如此類的應用場景很多,本文藉助快速排序的思想,給出解決此問題的一個時間複雜度爲O(N)的算法。

二、快速排序思想

    爲了引出本文基於快排TopK的思想,先給出快排的算法描述,令S爲待排序集合,|S|表示集合元素數量。

    1、如果 S 中元素個數是0或1,則返回。

    2、取 S 中任一元素 v, 稱之爲樞紐元。

    3、將 S - {v} (S中其餘元素) 分成兩個不相交的集合:S_1 = \left \{ x \in S - \left \{ v \right \} | x\leqslant v\right \}S_2 = \left \{ x \in S - \left \{ v \right \} | x\geqslant v\right \}

    4、返回 {quicksort(S_1)後,繼隨 v, 繼而quicksort(S_2) }。

    本人博客https://blog.csdn.net/gaoxueyi551/article/details/46778181中的代碼便是該思想的實現,可供參考。

 

三、基於快速排序的TopK算法

     其實,只要稍加修改快排算法,即可實現平均時間複雜度爲O(N)的TopK算法,我們稱之爲QuickSelect(S, K)。

     1、如果|S| = 1,返回S,否則若|S| < 20,則使用選擇排序對S排序,選擇最大的K個元素返回。(同快速排序步驟1)

     2、選取一個樞紐元 v 屬於 S。(同快速排序2)

     3、將集合 S - {v} 分割成 S1 和 S2。 (同快速排序步驟3)

     4、如果K <= |S1| ,則K個元素必然全部位於集合S1中,並返回QuickSelect(S1, K);如果K = |S1| + 1,則集合S1 與 樞紐元 v 恰好是所求的K個數,我們將 S1 和 v 一併返回;如果K > |S1| + 1,那麼S1和樞紐元v必然是K個元素的一部分,剩餘K - |S1| - 1 個元素必然存在集合 S2 中,因此我們應該返回 S1 + v + QuickSelect(S2, K - |S1| - 1)。

     可以看到,該算法前3步和快速排序是相同的,唯一區別在於步驟4。仔細分析該算法可得如下結論:

  •   快速選擇算法的遞歸調用次數是快速排序的一半。
  •   兩種算法的最壞情形都是O(N*N),即集合已排序的情況。
  •   算法平均時間複雜度爲O(N)。

     結論2易於理解。分析算法可知,每一輪遞歸開始之前,我們已經消去了1個集合,遞歸僅作用在另外一個集合,結論2於是成立。結論3的推導依賴結論1。

     在理想情況下,樞紐元 v 的選擇使每輪遞歸都近似的將當前集合等分,故最多需要遞歸O(logN)次:

         第1次遞歸對應的集合長度爲 |S / 2|,

         第2次遞歸對應的集合長度爲 |S / 4|,

         第3次遞歸對應的集合長度爲 |S / 8|,

         ......

         第log(N) 次遞歸對應的集合長度爲 1,

     將O(logN)次遞歸的集合長度相加,

         Sum = |S / 2| + |S / 4| + |S / 8| + ...... + 1

     這是等比數列,根據等比數列求和方法可得Sum大約等於2N,故快速選擇的平均時間複雜度爲O(N)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章