歸併排序(MergeSort)的原理及延伸性思考

2021-08-26 04:40:42 字數 3125 閱讀 3352

前面一篇博文寫了歸併排序的演算法實現,雖然做了些注釋,但沒有寫歸併排序的原理,這篇就補上,同時對歸併所隱含的思想做乙個**。

1)歸併排序的原理

為了便於說明,這裡我們提到的已排好序的序列都是指從小到大的公升序(對於降序其實原理是一樣的。

假設有兩個已排好序的序列a,b:

a:a1≤a2≤a3≤...≤an (i:1~n為下標);

b:b1≤b2≤b3≤...≤bm(j:1-m為下標);

如果我們要對a,b進行合併為c,並使得序列c是排好序的,這種情況下就很簡單了,我們只要取兩個序列的開頭進行比較,誰小就取誰,相等任意取乙個。然後對剩下的序列繼續按照上述原則進行,直到兩個序列都取完。比如:

開始:a: b: c{} ......a的第乙個元素2小於b的第乙個元素3,則a的第乙個元素2入c:

①:a: b c ......a的第乙個元素5大於b的第乙個元素3,則b的第乙個元素3入c:

②:a: b c ......a的第乙個元素5小於b的第乙個元素6,則a的第乙個元素5入c:

③:a: b c ......a的第乙個元素7大於b的第乙個元素6,則b的第乙個元素6入c:

④:a: b c ......a的第乙個元素7小於b的第乙個元素8,則a的第乙個元素7入c:

⑤:a: b c ......a的第乙個元素9大於b的第乙個元素8,則b的第乙個元素8入c:

⑥:a: b{} c ......b為空,則把a依次放入c(同理如果a先為空則依次將剩餘的b放入c):

⑦:a:{} b{} c .....排序合併完

上述的過程其實就是歸併排序的名稱得來,對於已經排好序的序列a(n),b(m),其合併排序時間為t(k)=m+n,屬於θ(n)級,是相當快的,但問題是,我們需要排序的序列p(n)未必都是剛好是排好序的兩個段(p1[1~m]和p2[m+1~n],而且就是已排好序,你還得去找這個分割點m.那是不是上面講的這些都白講了呢?當然不是,這個歸併的優勢需要結合其它的策略才行。

2)歸併排序所蘊含的思想

我們利用歸納法的思想,來思考一下上面所遇到的問題(歸納和演繹是兩種非常基本,也非常重要的方法或思想)。

我們從n=1開始分析:

a)如果序列只有乙個元素,即n=1,那麼歸併不存在任何問題;

b)如果序列有2個元素,n=2,利用歸併也不存在任何問題,因為m為1,p1[1-1],p2[2-2]兩個段顯然都是排好序的序列,而且m也很好找,就可以進行歸併,也沒問題;

c)如果序列有三個元素,n=3,有兩種情況:p1[1-1],p2[2-3]和p1[1-2],p2[3-2],這兩種情況其實是等價的(其實就是乙個段2個元素,乙個段1個元素),我們只需要分析一種情況即可。對於只有乙個元素的段,我們是可以知道其是排序的,而對於有兩個元素的段,就不能確定了,顯然也沒辦法進行歸併,但在這裡,我們從b可以知道,p2[2-3]本身是可以進行歸併的,如果我們先對其進行歸併排序,那麼p1和p2就可以進行歸併了。

d)從c並結合遞迴的思想,我們發現,對於任意序列p(n),我們總可以對其進行分割,如果分割的兩個段不能歸併,我們可以分別對兩個段各自分割,通過這種方法,我們最終可以將p(n)分割成n個片段,每個片段只包含1個元素,然後兩兩歸併最終完成整個序列的歸併排序,這其實就是二分法。通過二分法,我們可以將p(n)做如下分割:

如上圖所示,進行二分後,會形成一棵樹,每一級的元素個數總數為n,那麼每一級的歸併t(n)都是θ(n),而深度(樹的深度),通過右邊深度很容易得出是lg(n),那麼整個歸併過程的t(k) =lgk*θ(k)=θ(lgk*k)(k就是n這裡為了顯示清楚).我們知道插入排序(insertionsort)的t(k)=θ(k^2),lgk3)歸併排序的延伸性思考

歸併排序採用二分法縮小排序規模,從而利用已排序序列歸併演算法以實現排序加速。而縮小規模的最基本方法就是n分法(n>1),二分法是最簡單的,當然還有三分法,甚至是n分法,但並不是n越大越好,至於分的時候如何取n,就要看n本身是不是最基本的,對於本例來說,n=2的時候顯然可行,對於n=3,那麼我們需要對3個序列進行歸併排序,每個段的元素(小於等於3個)本身也要排序。(其實歸併時候的排序本身就是乙個插入排序,而且它是插入排序最理想的情況)。

我們可以觀察更為一般的情況,設規模n=k^m【注意這裡是為了簡化運算,最後會代換回到一般情況】,我們採用k分法,n=k,對於第i級(i=1..m):

有k^(i-1)個片段,每個片段元素數量為k^m/(k^(i-1)=k^(m-i+1)=ni個元素;但每級元素總數不變,為k^m個.相當於每次從k個元素中取1個最小的,其t(n)=k-1,則一級歸併時間是:(k^m * (k-1)),則整體時間為t(n)=(m * k^(m) *(k-1)).因為m=log(k,n),帶入上式:t(n)=log(k,n)*k^log(k,n)*(k-1)=log(k,n)*n*(k-1)=(k-1)log(k,n)*n.從這個表示式可以看出,隨著k變大,雖然log(k,n)可以降低速度,但k卻會增加速度。k=2為:lgn * n,k=3時t(n)=2*log(3,n)*n,n=n時,t(n)=(n-1)*n=θ(n^2),在n分時其實就是插入排序時間複雜度.

上面的情況中我們假設了規模n=k^m,m不一定會是整數,但這個並不影響時間複雜度的判斷,原因大家可自己分析。

總結:歸併排序利用對於已排序的序列進行排序的最理想情況,並結合n分法思想解決問題,提高效率。n分法思想是降低規模常用的手段,在演算法中非常重要,比如查詢樹之類的(更進一步講,這就是演算法中的分治策略的一種)。一般情況下,二分法就足夠了,但在很多規模n非常大的情況下,3分法甚至更高的分法還是有用,不過隨著n加大,演算法處理的複雜度本身也會加大,因此必須綜合權衡。

上述的n分法θ時間表示式是我推理出來的,實際情況中每個分段得元素不太可能正好是相等的,每個片段的元素其實可能在1..ni中,但我考慮最大情況,就不會影響最後的結論,因為實際情況只可能比考慮的情況好。如果大家覺得上述推理有不妥之處,還望大家指正。

(具體二分法歸併,可參見前面的博文)

後記:晚上繼續看公開課的第2-3集,看完之後真的汗,別人那是有數學支撐的,雖然我的推論結果是對的,但對於漸進符號,遞迴解法早就忘了,而我上面說的n分法只是分治策略的一種,看來還是要正規學習。

另外歸併排序的遞迴表示式為:t(n)=2t(n/2)+θ(c)=θ(nlgn). 其中2表示二分分治後需要處理的策略數。那麼對於k分法同樣可以表達為(t(n)=kt(n/k)+θ(c). 大家注意這是k分法的最壞情況,有的時候雖然是k分後,但需要處理的策略並不是等於k,而是小於等於k.比如二分查詢的時間遞迴表示式就是t(n)=t(n/2)+θ(c)=θ(lgn).

歸併排序 Merge sort

merge the a s.m and a m 1.t to r s.t template void two merge typet a,typet r,int s,int m,int t while i m r k a i while j t r k a j merge the a 0.n 1 s...

歸併排序(merge sort)

歸併排序 歸併排序是一種遞迴排序演算法,無論陣列元素的原始順序如何,其效能恆定不變。將陣列一分為二,分別排序兩部分元素,再將有序的兩半陣列歸併為乙個有序陣列。歸併步驟比較陣列前一半的元素與陣列的後一半元素,並將較小元素移到臨時陣列,該過程繼續前進,直到其中一半再沒有元素為止。此後只需將其餘元素移到臨...

歸併排序 Merge Sort

歸併排序 merge sort 是利用 歸併 技術來進行排序。歸併是指將若干個已排序的子檔案合併成乙個有序的檔案。兩路歸併演算法 1 演算法基本思路 設兩個有序的子檔案 相當於輸入堆 放在同一向量中相鄰的位置上 r low.m r m 1.high 先將它們合併到乙個區域性的暫存向量r1 相當於輸出...