線性時間查詢

漆楚衡發表於2014-11-23

問題描述:

給定線性序集中n個元素和一個整數k,1 <= k <= n,要求找出這n個元素中第k小的元素,即如果將這n個元素以其線性序列排列時,排在第k個位置的元素係為要找的元素。當k = 1時,就是要找最小元素,當k = n時,就是要找最大元素;當k = (n + 1) / 2時,稱為找中位數。

分析:

先考慮一個時間要求不嚴格的演算法,由快速排序可以得到啟發:

  • 每次依照某個基準元素將序列劃分為兩部分
  • (1):一部分小於等於基準
  • (2):一部分大於基準
  • 接下來就可以只處理這兩部分中的一部分,如果:
    • |(1)| < k,則k一定在(2)中,問題變成(2)中的k - |(1)|選擇。
    • 否則問題變成(1)中的k選擇。
  • 這樣問題就可以遞迴求解了。

但是這個問題和快速排序有同樣的弊端:最壞情況下會有O(n ^ 2)的時間複雜度。這主要取決於基準的選擇。

針對這樣的問題,第一種應對辦法也是與快速排序一樣,隨機化基準選擇。但(從最壞分析的形況看)這不能根本解決問題。

現在考慮如何能夠縮小劃分基準,實際上,我們不必希望劃分基準能夠總是均分序列,只要能夠保證總是將序列至少划走某一比例(0 < e < 1)就行了。

於是,有k = 1, m = e^-1

T(1) = O(1) (n = 1)

T(n) = T(n * e) + O(n) (n > 1)

因為k為1,即將T(n)完全展開有:

O(n) + O(n * e) + O(n * e ^ 2) + O(n * e ^ 3) + ... + O(1)

這是一個幾何級數,其加和與n同階,即演算法複雜度為O(n)

如何尋找這樣一個基準?

方法是建立一種二級機制選中位數機制(以下討論假設沒有重複元素):

  • 先將問題序列P按某常數a分割為|P| / a組,即每組a個元素(最後一組可能不足a個元素)
  • 對這些組分別排序,因為a是常數,每組的排序時間消耗也為O(1),總時間消耗為O(n)
  • 選出這|P| / a組的中位數,用本演算法找出它們的中位數(mid),這一部分的複雜度為T(n / a)

因為上述步驟的mid大於一半的中位數,也大於對應小陣列的一半,即保證mid大於P中1 / 4的元素,即保證劃分後至少將P縮減1 / 4,而整個演算法有

T(1) = O(1) (n = 1)

T(n) = T(n / a) + T(n * 3 / 4) + O(n) (n > 1)

根據前面的討論:e = 1 / a + 3 / 4 < 1,即a > 4

重複情況分析:

剛才的討論中要求元素不重複,實際上,如果元素重複,演算法有可能失效(等於基準的元素過多則不保證能夠划走至少1 / 4,比如當所有元素相同時,演算法將無法終止)。不過修正這一點也不困難,只要在劃分完成後,且進入(1)中部分,則對等於基準的元素進行考察,設有x個等於基準的元素集中在(1)的尾部,如果|(1)| - x < k,則返回k,否則將(1)中等於基準的元素去掉再求解。

補充

可以看到這個演算法常數比較大,所以,可以考慮當n小於某個閾值後將演算法改為排序+取第k位。

疑惑

書上說(書上取a = 5且有閾值 = 75作為分界):

由於演算法將每一組的大小定為5,並去75作為是否進行遞迴呼叫的分界點。這兩點保證了T(n)的遞迴式中兩個自變數之和n / 5 + 3n / 4 = 19n / 20 = an, 0 < a < 1。這是使T(n) = O(n)的關鍵之處。

對於這裡75的作用我還不理解,對於T(n) = T(n / a) + T(n * 3 / 4) + O(n) (n > 1)e = 1 / a + 3 / 4 < 1也不能講清楚。

相關文章