排序演算法學習 歸併排序

2021-08-15 15:37:46 字數 2283 閱讀 7287

歸併排序(merge sort),採用分治思想,將排序的過程分成乙個個子過程,當所有子過程完成時,排序也最終完成了。而歸併排序是將陣列不斷地二分,進而形成乙個個子過程。

子過程,即歸併的過程如下圖演示,需要額外開闢一片空間進行複製乙份原陣列,然後兩邊逐一進行比較後進行歸併。

至於它的時間複雜度,我們可以看到它的樹的高度應該是lo

g2n log

2n這時候我們使用時間複雜度為o(n)的演算法進行排序即可完成排序。

將arr[l,mid]和arr[mid+1,r]兩部分進行歸併

*/templatevoid __merge(t arr, int l, int mid, int r)

//初始化索引位置,i為左側索引,j為右側

int i = l, j = mid + 1;

//k為應該插入arr的位置

for(int k = l; k <= r; k++) else

if(j > r) else

if(tem[i - l] < tem[j - l]) else

}}/**

遞迴使用歸併排序,對arr[l,r]範圍進行排序

l,r為閉區間,r表示最後乙個而非最後乙個的後面乙個

*/templatevoid __mergesort(t arr, int l, int r)

//注意l+r可能會溢位

int mid = (l + r)/2;

//遞迴二分

__mergesort(arr, l, mid);

__mergesort(arr, mid + 1, r);

//優化**1

if(arr[mid] > arr[mid + 1])

//歸併左右兩部分

__merge(arr, l, mid, r);

}templatevoid mergesort(t arr, int n)

上面給出了歸併排序的實現,同時優化已經包含在內。

//優化**1

if(arr[mid] > arr[mid + 1])

//歸併左右兩部分

__merge(arr, l, mid, r);

優化**1在歸併處,如果左半邊的最右(左半邊最大的數)小於右半邊的最左(右半邊最小的數),那麼是不是已經是有序的呢?因此不需要再進行不必要的歸併,減少了很多冗餘的工作。加了一行**就完成了針對近乎有序陣列的優化。

//跳出條件

if(l >= r)

return;

這是原來的跳出條件,也是根據定義,當二分到只有乙個數的時候應該跳出迴圈。但是我們可以在這裡使用插入排序進行一點優化,這基於兩點原因:

在資料量較小的情況下,近乎有序的概率較高

雖然插入排序的時間複雜度為o(n^2)級別的,但是前面的係數較小,當資料量小的時候比歸併排序更快

//優化**2,陣列較小的時候用插入代替歸併

if( r - l <= 15)

這裡的數值我本來想做一下實驗的,不過受限於編譯器,我使用的是codeblocks,時間只顯示了四位小數,因此數值很小的時候都顯示為0s……因此就使用了15這個數了,我是抱著學習原理的態度來學的,這裡也沒花太多時間,有興趣大家可以實驗一下,到底哪個數最優。

上面我們是從上到下,不斷地遞迴分層,然後歸併。我們同樣可以從下而上,先兩個兩個歸併,然後四個四個,不斷增加步長完成歸併。演示如下圖所示:

自底而上的歸併排序

沒有使用arr的索引,可用於鍊錶的排序

*/template void mergesortbu(t arr, int n) }}

歸併仍然使用__merge方法,步長sz從1開始不斷*2,最終長度大於等於整個陣列長度時停止。

這裡我們並沒有針對近乎有序的情況進行優化,所以速度稍微慢了一點。不過自底而上的歸併排序有乙個優勢,它在實現的過程中沒有使用陣列下標,因此在遷移至鍊錶的排序時優勢會比較大。

演算法學習(排序二)歸併排序

歸併排序所採用的思想是分治法,即 分解 解決 合併 假設面對乙個無序的陣列,如,將其分解為左右兩個陣列,但此時這兩個陣列還是無序的,因此,還需要進一步的分解,經過若干步之後,分解後如下所示 當每個陣列中僅有乙個元素的,可以認為此陣列是有序的,接下來,這些陣列兩兩合併,最後乙個組合成乙個完整的有序陣列...

遞迴演算法學習(歸併排序)

歸併排序是利用遞迴和分而治之的技術將資料序列劃分成為越來越小的半子表,再對半子表排序,最後再用遞迴步驟將排好序的半子表合併成為越來越大的有序序列,歸併排序包括兩個步驟,分別為 1 劃分子表 2 合併半子表 首先我們來討論歸併演算法,歸併演算法將一系列資料放到乙個向量中,索引範圍為 first,las...

演算法學習筆記 歸併排序

歸併排序 一種簡單的利用遞迴排序的演算法,將乙個陣列先 遞迴地 將它分成兩半分別排序,然後將兩半分別排序,然後將結果歸併起來。原地歸併的抽象方法 public static void merge int a,int lo,int mid,int hi 歸併回a lo.hi for int k lo ...