最優排序
前面討論了很多的排序方法,那麼有沒有什麼方法是最優的呢?有沒有什麼辦法排序是最快的呢?
答案顯然是否定的,什麼才算是最優呢?一個排序算法受很多因素的影響,也可以從很多的角度來衡量一個排序算法,有些可能關注比較的次數,有些可能關注記錄移動的次數,有些關注額外空間的使用。因此沒有最優的排序算法,只有最合適的。但是排序算法中經常關注的就是排序過程中的比較次數,下面討論一下在基於比較的排序算法中如何用最少的比較次數來排序。
對N個元素的數組進行比較排序,最終得到有序序列的過程都可以用一個二叉樹來表述,如下圖所示,其中內節點的兩個下標i:j表示Ki 和Kj進行比較,左子樹表示Ki<Kj,右子樹表示Ki>Kj, 葉子節點表示最終排序後數組下標值,例如a1, a2, ..., an表示Ka1 < Ka2<...<Kan
首先比較K1 K2,如果K1<K2,走左子樹,K2和K3比較,如果K2>K3,走右子樹,比較K1 K3,K1<K3,則我們知道K1<K3<K2。我們發現2:3比較完之後並沒有繼續比較1:3,因為K1<K2 而K2<K3,我們已經知道K1<K3了,所以沒必要再比較K1 K3了,這是冗余的情況,因此這樣的路徑是永遠走不到的,而我們關心的是最少比較次數,所以這樣的情況直接從圖中剔除掉了。
這樣一棵二叉樹的內節點表示了所有可能的輸入情況下得到最終有序序列的比較方式, 那麼剔除掉冗余情況之後,對n個元素的數組排序,二叉樹葉節點的個數為n!,也就是n個元素的所有排列組合情況。
那麼接下來的問題就是在所有可能的排序二叉樹中,每棵樹的最大比較次數裡面的最小值是多少呢?這個最小值也就是對n個元素進行排序的最壞情況下的最少比較次數。
假設這個最小值為S(n),如果這棵排序二叉樹的內節點的層數都<K,顯然這棵樹最多有2k個葉節點。即n! <=2s(n)
因為S(n)是整形,那麼S(n)>=log2n!取上限。
在前面介紹的算法中,在最壞情況下擁有最少比較次數算法是binary insertion, tree selection, two-way merging, binary insertion的比較次數為:
在n不是很大的情況下,我們可以計算出比較的次數為:
對於S(4)可以用5次比較得到最終結果,對於S(5),可能是7也可能是8, 那麼5個數排序,如何用7次比較來得到結果呢?
首先比較K1 K2然後比較K3 K4兩對的最大值再進行比較,總共比較3次,這樣我們可以得到下面的圖形:
a到b的箭頭表示a<b,接著我們把e插入到a b d之間,那麼可能會有下面四種情況:
最多比較兩次,e先和b比較,大的話則和d比,小的話則和a比。
最後把c插入到a b d e之間,我們可以發現最多也只需要比較兩次,c和e比,小的話則和a比,否則和b比,c和d已經比較過了,不用再比較了。因此總的次數就是3+2+2=7次
那麼把這一方法推廣到一般化的情況下呢?我們稱其為merge insertion方法,下面來看一個21個元素的例子
先取出前10對元素,K1:K2 K3:K4 ... K19:K20兩兩比較找出最大的,需要10次比較,然後對這些最大的元素使用merge insertion方法排序,需要22次,可以得到下圖:
之後將b3插入到b1 a1 a2之間,和前面討論的一樣,需要2次,然後把b2插入進去,也是2次,這樣可以得到下面的序列:
我們把最上面一層箭頭所指的元素稱為主鏈,也就是已經排好序的元素,接下來可以把b5插入到主鏈中,需要3次比較,先和c4比,小的話再和c2比,小的話和c1比,否則和c3比,如果比c4大,則和c6比,比c6大則和a4比,否則和c5比,類似的我們可以用3次比較把b4插入到主鏈。得到下圖:
接下來是最關鍵的一步,我們把b11插入到主鏈,而不是b6,使用4次比較,既先和d8比,然後和d4或a7比,然後和d2或者d6或者d10或者a9比,最後再比一次,總共4次。接下來依次將b10 b9 b8 b7 b6插入到序列中,每個需要4次,因此總的比較次數為20+22+2+2+3+3+4+4+4+4+4+4=66次,因為265<21!<266
因為S(21)=66,因此我們 對21個數進行排序,最少要66次。為什麼要先插入b11呢?我們可以發現,主鏈上面的元素為15個,通過二分法,4次比較可以找到合適的插入位置,但是如果先插入b6到b10之間的元素,則主鏈元素為16個,最壞要5次才能把b11插入進去了。那麼為什麼先插入b10而不是b6呢?當b11插入到主鏈之後,主鏈個數為16個,但是b10和a10已經比較過了,因此只要在前面15個元素裡面找到合適的位置,只需要4次,而如果先插入b6的話,b6前面有10個元素,最壞也要4次,即使把後面5個元素都插入到a6之前也才15個,也只要4次,因此我們插入的順序是b11 b10 ... b6,這樣將最壞情況下的總的比較次數降到了最低。把這最關鍵的地方弄清楚之後,就可以開始推廣到n的情況了,下面就是merge insertion算法的描述
1、將n個元素兩個一對比較,找出每對最大的如果n為偶數,則最後沒有剩余的元素)
2、通過merge insertion對這2/n個數進行排序
3、對這些元素分別命名為a1,a2,...,a2/n b1,b2, ... , b2/n如果n為奇數則是b(2/n+1) 其中a1 < a2 < ... < a2/n ai<bi
b1和a一起稱為主鏈,後面的工作就是要把其余的b使用二分插入法插入到主鏈中去。剩余元素的插入順序為:
接下來的任務就是要定義(t1, t2, t3, t4,...)=(1,3,5,11,...)這樣一個序列,那麼就可以按照
的順序使用k次比較來將這些元素插入到主鏈中去了,這是這個鏈如下圖所示:
我們知道主鏈包含節點At(k-1)的總節點個數為:2Tk-1 + (Tk-Tk-1 -1) =Tk-1 + Tk -1 這個數肯定小於2k,最好等於2k-1
既:Tk-1 + Tk = 2k
T1=1 我們假設T0=1
這樣就可以求出這個序列的取值了。
總結:這是昨天晚上看《The Art of Computer Programming》時看到了,理解了很久才算入了點門,後面的分析略過去了,有興趣的朋友可以找到書看,算法的思想確實很巧妙,而且分析得相當的透徹,我們平時也沒有誰會為了節省幾次比較操作而去研究如何讓排序過程中的比較次數最少吧,最關鍵的還是通過這個算法了解作者如何分析問題,推廣問題,最終解決問題的方法。