歸並排序 (merge sort) 是一類與插入排序、交換排序、選擇排序不同的另一種排序方法。歸並的含義是將兩個或兩個以上的有序表合並成一個新的有序表。歸並排序有多路歸並排序、兩路歸並排序 , 可用於內排序,也可以用於外排序。這里僅對內排序的兩路歸並方法進行討論。
一、兩路歸並排序算法思路
分而治之(divide - conquer);每個遞歸過程涉及三個步驟
第一, 分解: 把待排序的 n 個元素的序列分解成兩個子序列, 每個子序列包括 n/2 個元素.
第二, 治理: 對每個子序列分別調用歸並排序MergeSort, 進行遞歸操作
第三, 合並: 合並兩個排好序的子序列,生成排序結果.
二、算法實現
此算法的實現不像圖示那樣簡單,現分三步來討論。首先從宏觀上分析,首先讓子表表長 L=1 進行處理;不斷地使 L=2*L ,進行子表處理,直到 L>=n 為止,把這一過程寫成一個主體框架函數 mergesort 。然后對於某確定的子表表長 L ,將 n 個記錄分成若干組子表,兩兩歸並,這里顯然要循環若干次,把這一步寫成一個函數 mergepass ,可由 mergesort 調用。最后再看每一組(一對)子表的歸並,其原理是相同的,只是子表表長不同,換句話說,是子表的首記錄號與尾記錄號不同,把這個歸並操作作為核心算法寫成函數 merge ,由 mergepass 來調用。假設我們有一個沒有排好序的序列,那么首先我們使用分割的辦法將這個序列分割成一個一個已經排好序的子序列,然后再利用歸並的方法將一個個的子序列合並成排序好的序列。分割和歸並的過程可以看下面的圖例。
三、代碼實現
public static int[] sort(int[] a,int low,int high){ int mid = (low+high)/2; if(low<high){ sort(a,low,mid); sort(a,mid+1,high); //左右歸並 merge(a,low,mid,high); } return a; } public static void merge(int[] a, int low, int mid, int high) { int[] temp = new int[high-low+1]; int i= low; int j = mid+1; int k=0; // 把較小的數先移到新數組中 while(i<=mid && j<=high){ if(a[i]<a[j]){ temp[k++] = a[i++]; }else{ temp[k++] = a[j++]; } } // 把左邊剩余的數移入數組 while(i<=mid){ temp[k++] = a[i++]; } // 把右邊邊剩余的數移入數組 while(j<=high){ temp[k++] = a[j++]; } // 把新數組中的數覆蓋nums數組 for(int x=0;x<temp.length;x++){ a[x+low] = temp[x]; } }
四、算法分析
(1)穩定性
歸並排序是一種穩定的排序。
(2)存儲結構要求
可用順序存儲結構。也易於在鏈表上實現。
(3)時間復雜度
對長度為n的文件,需進行趟二路歸並,每趟歸並的時間為O(n),故其時間復雜度無論是在最好情況下還是在最壞情況下均是O(nlgn)。
(4)空間復雜度
需要一個輔助向量來暫存兩有序子文件歸並的結果,故其輔助空間復雜度為O(n),顯然它不是就地排序。
注意:
若用單鏈表做存儲結構,很容易給出就地的歸並排序