選擇問題定義,實際上所有處理均可以推廣到集合中包含重復數值的情形。
輸入:一個包含n個(不同的)數的集合A和一個數i,1<=i<=n。
輸出:元素x屬於A,它恰大於A中其他的i-1個元素。
1、最大值最小值
針對一個序列取得最大和最小值均需要n-1次比較。這是一個下限,確定最大值或者最小值的算法可以看作各個元素之間一場錦標賽,每次比較都是一場比賽,兩個元素中較小的或者較大的獲勝,除了最終的最大值和最小值,所有其他元素都需要輸一次,所以n-1次是必須的。
接下來是一些比較有意思的問題,比如同時找出最小值和最大值,當然可以n-1次比較找出最大值,然后n-2次比較找出最小值,不過還是有比這個更好一點的算法,把元素兩兩分組,然后比較產生一個較大的值和較小的值,然后較大的值中產生最大值,較小的值中產生最小值,此時需要比較操作的次數至多3|_n/2_|。
還有一個比較問題是同時找出最大第二大或者最小次小元素的比較次數,簡單的當然是2n-3,不過也有一個分組的方法能夠達到n+lgn-2的比較次數。比較方法如下:
上面已經說明了,找出最值最少的比較次數n-1,所以上面尋找的方法也是n-1次,不信可以累計求和,不過這樣求最值的過程中最值上來的時候有一條路徑被記錄,這條路徑的長度為lgn,找出次大值或者次小值直接在這個路徑上尋找就只需要lgn-1的比較次數。
2、線性時間選擇
上面只是拋磚引玉,接下來才是選擇問題的具體算法,分別是一個期望線性時間和最壞情況線性時間的選擇算法。
期望選擇時間偽代碼(算法里的partition和快速排序里的partition一樣的方法)
RANDOMIZED-SELECT(A,p,r) if p = r then retrun A[p] q= RANDOMIZED-PARTITION(A,p,r) k= q-p+1 if i = k then return A[q] else if i<k return RANDOMIZED-SELECT(A,p,q-1,i) else return RANDOMIZED-SELECT(A,q+1,r,i-k)
RANDOMIZED-PARTITION(A,p,r) i=RANDOM(p,r) exchange A[r],A[i] return PARTITION(A,p,r) PARTITION(A,p,r) x=A[r] i=p-1 for j=p to r-1 do if A[j]<=x i++ exchange A[i],A[j] exchange A[i+1],A[r] return i+1
上面方法的期望時間是線性的,數學證明比較復雜就省略了。
最壞情況線性時間
上面期望時間線性是因為可能存在某種最壞的情況,每次分割元的位置都是導致一邊沒有元素,這樣就不能夠達到線性了,接下來的一些預先工作就是避免這種情況發生。
算法SELECT通過執行下列步驟來確定一個有n個元素的輸入數組中的第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)個最小元素。
圖中箭頭指向表示大的數值指向小的數值,所以根據圖可以看出,在x的右邊,每一個包含5個元素的組中至少有3個元素大於x,x的左邊,每一組中至少有3個元素小於x。(保證x分割一邊必定有元素存在)
圖中顯示的中位數的中位數x的位置,每次選取x作為划分的好處是能夠保證必定有一部分在x的一邊。
所以算法最壞情況的遞歸公式可以寫成:
使用替換法可以得出T(n)<=cn。
以上內容參考算法導論,轉載請注明出處。