【排序演算法】歸併排序(C++實現)
歸併排序是利用"歸併"技術來進行排序。歸併是指將若干個已排序的子檔案合併成一個有序的檔案。常見的歸併排序有兩路歸併排序(Merge Sort),多相歸併排序(Polyphase Merge Sort),Strand排序(Strand Sort)。下面介紹第一種:
(一)兩路歸併排序
最差時間複雜度:O(nlogn)
平均時間複雜度:O(nlogn)
最差空間複雜度:O(n)
穩定性:穩定
兩路歸併排序(Merge Sort),也就是我們常說的歸併排序,也叫合併排序。它是建立在歸併操作上的一種有效的排序演算法,歸併操作即將兩個已經排序的序列合併成一個序列的操作。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。
歸併操作的基本步驟如下:
1.申請兩個與已經排序序列相同大小的空間,並將兩個序列拷貝其中;
2.設定最初位置分別為兩個已經拷貝排序序列的起始位置,比較兩個序列元素的大小,依次選擇相對小的元素放到原始序列;
3.重複2直到某一拷貝序列全部放入原始序列,將另一個序列剩下的所有元素直接複製到原始序列尾。
設歸併排序的當前區間是R[low..high],分治法的三個步驟是:
1.分解:將當前區間一分為二,即求分裂點
2.求解:遞迴地對兩個子區間R[low..mid]和R[mid+1..high]進行歸併排序;
3.組合:將已排序的兩個子區間R[low..mid]和R[mid+1..high]歸併為一個有序的區間R[low..high]。
遞迴的終結條件:子區間長度為1(一個記錄自然有序)。
演算法示意圖:
程式碼實現:
void Merge(int *a, int p, int q, int r) { int n1 = q-p+1; int n2 = r-q; int *L = new int[n1+1]; int *R = new int[n2+1]; int i, j, k; for (i=0; i<n1; i++){ L[i] = a[p+i]; } for (j=0; j<n2; j++){ R[j] = a[q+j+1]; } L[n1] = 10000000; R[n2] = 10000000; for (i=0, j=0, k=p; k<=r; k++) { if (L[i]<=R[j]) { a[k] = L[i]; i++; }else{ a[k] = R[j]; j++; } } delete []L; delete []R; } void MergeSort1(int *a, int p, int r) { if (p<r) { int q = (p+r)/2; MergeSort1(a, p, q); MergeSort1(a, q+1, r); Merge(a, p, q, r); } }
雖然插入排序的時間複雜度為O(n^2),歸併排序的時間複雜度為O(nlogn),但插入排序中的常數因子使得它在n較小時,執行得要更快一些。因此,在歸併排序演算法中,當子問題足夠小時,採用插入排序演算法就比較合適了。
程式碼實現:
void MergeSort2(int *a, int p, int r) { if ((r-p)>=50) // 小於50個數據的陣列進行插入排序 { int q = (p+r)/2; MergeSort2(a, p, q); MergeSort2(a, q+1, r); Merge(a, p, q, r); }else { InsertionSort(a+p, r-p+1); } }
MergeSort1與MergeSort2演算法排序時間實驗結果比較:
資料量 |
1K |
10K |
100K |
1000K |
10000K |
MergeSort1 |
0.001s |
0.008s |
0.065s |
0.552s |
5.875s |
MergeSort2 |
<0.001s |
0.001s |
0.021s |
0.219s |
2.317s |