最優排序
前面討論了很多的排序方法,那麼有沒有什麼方法是最優的呢?有沒有什麼辦法排序是最快的呢?
答案顯然是否定的,什麼才算是最優呢?乙個排序演算法受很多因素的影響,也可以從很多的角度來衡量乙個排序演算法,有些可能關注比較的次數,有些可能關注記錄移動的次數,有些關注額外空間的使用。因此沒有最優的排序演算法,只有最合適的。但是排序演算法中經常關注的就是排序過程中的比較次數,下面討論一下在基於比較的排序演算法中如何用最少的比較次數來排序。
對n個元素的陣列進行比較排序,最終得到有序序列的過程都可以用乙個二叉樹來表述,如下圖所示,其中內節點的兩個下標i:j表示
ki 和kj進行比較,左子樹表示kikj, 葉子節點表示最終排序後陣列下標值,例如a1, a2, ..., an表示ka1 < ka2<...>
首先比較k1 k2,如果k1k3,走右子樹,比較k1 k3,k1這樣一棵二叉樹的內節點表示了所有可能的輸入情況下得到最終有序序列的比較方式, 那麼剔除掉冗餘情況之後,對n個元素的陣列排序,二叉樹葉節點的個數為n!,也就是n個元素的所有排列組合情況。
那麼接下來的問題就是在所有可能的排序二叉樹中,每棵樹的最大比較次數裡面的最小值是多少呢?這個最小值也就是對n個元素進行排序的最壞情況下的最少比較次數。
假設這個最小值為s(n),如果這棵排序二叉樹的內節點的層數都2
k個葉節點。即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
最多比較兩次,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 aib1和a一起稱為主鏈,後面的工作就是要把其餘的b使用二分插入法插入到主鏈中去。剩餘元素的插入順序為:
接下來的任務就是要定義(t1, t2, t3, t4,...)=(1,3,5,11,...)這樣乙個序列,那麼就可以按照
的順序使用k次比較來將這些元素插入到主鏈中去了,這是這個鏈如下圖所示:
我們知道主鏈包含節點at(k-1)的總節點個數為:
2tk-1+ (tk-t
k-1-1) =
tk-1+ t
k-1 這個數肯定小於2
k,最好等於2
k-1
既:tk-1+ tk=
2kt1=1 我們假設t0=1
這樣就可以求出這個序列的取值了。
總結:這是昨天晚上看《the art of computer programming》時看到了,理解了很久才算入了點門,後面的分析略過去了,有興趣的朋友可以找到書看,演算法的思想確實很巧妙,而且分析得相當的透徹,我們平時也沒有誰會為了節省幾次比較操作而去研究如何讓排序過程中的比較次數最少吧,最關鍵的還是通過這個演算法了解作者如何分析問題,推廣問題,最終解決問題的方法。
十大排序演算法之堆排序,最優實現
堆排序是利用堆這種資料結構而設計的一種排序演算法,堆排序是一種選擇排序,它的最壞,最好,平均時間複雜度均為0 nlogn 它也是不穩定排序。堆是具有以下性質的完全二叉樹 將待排序序列構造成乙個大頂堆 此時,整個序列的最大值就是堆頂的根節點。將其與末尾元素進行交換,此時末尾就為最大值。然後將剩餘n 1...
Dijastra最優路徑演算法
關於dijastra演算法,零零散散地研究了差不多兩天了,基本上弄懂了它的思路和寫程式的思路。演算法思路不是很難 沒理解時還是覺得有點晦澀 的實現過程,需要反覆推敲和琢磨。目前先寫個初級版本 現在暫時只理解到這個程度 演算法的思想和方法,各路牛人已經寫過很多,小菜鳥就不贅述了,對我自己而言,我覺得下...
最優化演算法 三
牛頓方法是現在用的比較廣泛的最優化演算法之一,其特點是收斂速度較快,上一節的梯度下降和隨機梯度下降都是一階收斂,而牛頓方法是二階收斂。回憶高等數學裡面介紹的二階泰勒展開有q x f x0 f x0 x x0 x x0 22 2f x 0 而 q x 取到極值的條件,也就是最優化條件是 q x 0 可...