聽說這是面試算法崗位中最常問的問題,各個面經裏面出現這個問題的比率很高,今天的算法課講了《算法導論》的第九章「中位數和順序統計量」,9.2和9.3偏偏介紹的就是這個問題,這裏作一下總結,說一下本身的理解。面試
首先,接手這個問題最直接的想法莫過於排序了,經常使用的排序算法裏面有冒泡排序、選擇排序、插入排序、歸併排序、堆排序、快速排序、計數排序、基數排序、桶排序等,雖說快排的平均時間複雜度達到了O(nlgn),桶排序的平均時間複雜度達到了O(n)可是基於排序的方法對於這個問題並非一個好的解決方法。O(nlgn)的排序就不說了,O(n)的排序前面隱含的常數因子也會被下面介紹的算法所戰勝。算法
接下來會想到的方法,多是對整個數組過i遍,每次都選擇最小的,這樣第i次就選到了第i小的,這樣的話時間複雜度就成了O(in),當i比較大時,仍然是不可接受的,萬一i接近n/2呢,豈不是都O(n2)了。數組
指望線性時間複雜度算法spa
思考一下快排的PARTITION過程,通過屢次左右互換,實現了針對於選定靶點A[q],左側的元素都小於A[q],右側的元素都大於A[q]。這個過程豈不是能用來幫助選點。code
1 RANDOMIZED-SELECT(A,p,r,i) 2 if p==r 3 return A[p] 4 q = RANDOMIZED-PARTITION(A,p,r) //劃分位置 5 k = q-p+1 6 if i==k //the pivot value is the answer 7 return A[q] 8 else if i<k 9 return RANDOMIZED-SELECT(A,p,q-1,i) 10 else return RANDOMIZED-SELECT(A,q+1,r,i-k)
第4行的劃分位置是隨機取到的,這樣就爲下面分析其指望時間複雜度埋下伏筆。k是left部分的長度。當i=k時,六、7行是k恰好知足長度要求,程序結束。第9行,意思是i<k時,程序進入left部分(注意遞歸處理的數組的範圍的變化爲A[p]~A[q-1]);當i>k時,進入right部分(注意遞歸處理的數組的範圍的變化爲A[q+1]~A[r],尋找的數的「位置」也要減去left部分的長度)。如此遞歸下去,則總會退出,即找到對應的第i小的數。blog
下面分析其時間複雜度。排序
最壞狀況:假如第4行的RANDOMIZED-PARTITION(A,p,r) 每次劃分的比例都是0:n-1(即最差狀況),這樣的話時間複雜度就是O(n2)。遞歸
平均狀況:T(n)≤Σk=1nXk•(T(max(k-1,n-k))+O(n) ) = Σk=1nXk•T(max(k-1,n-k)) + O(n) class
通過替換推導 E(T(n)) ≤ cn。即指望時間複雜度爲線性時間複雜度。程序
最壞狀況爲線性時間點的選擇算法
該算法比「指望線性時間複雜度算法」的主要改進是經過尋找中位數來獲得一個更好的劃分(上面的是隨機取的劃分位置)。SELECT算法使用的也是快速排序的肯定性劃分算法PARTITION,但作了修改,把劃分的主元也做爲輸入參數。
經過執行下列步驟,算法SELECT能夠肯定一個有n>1個不一樣的元素的輸入數組中第i小的元素。(若是n=1,則SEKECT只返回它的惟一輸入數值做爲第i小的元素 ) (1)將輸入數組的n個元素劃分爲(n/5)組,每組5個元素,且至多隻有一組由剩下的n mod 5個元素組成。 (2)尋找這(n/5)組中每一組中位數:首先對每組元素進行插入排序,而後肯定每組有序元素的中位數。 (3)對第2步中找出的(n/5)箇中位數,遞歸調用SELECT以找出其中位數x(若是有偶數箇中位數,爲了方便,約定x是較小的中位數) (4)利用修改過的PARTITION版本,按中位數的中位數x對輸入數組進行劃分。令k=劃分的低區中的元素個數+1,所以x是第k小的元素,而且有n-k個元素在劃分的高區。 (5)若是i=k,則返回x。若是i<k,則在低區遞歸調用SELECT來找出第i小的元素。若是i>k,則高區遞歸查找第i-k小的元素。
步驟一、2和4須要O(n)的時間。(步驟2是對大小爲O(1)的集合調用O(n)次插入排序) 步驟3所需時間爲T(n/5),步驟5所需時間至多爲T(7n/10+6)(具體推導請參考算導9.3)。這樣根據主方法就很容易推導出時間複雜度爲線性。(T(n)≤cn)