1、基本思想
歸併排序(Merge)是將兩個(或兩個以上)有序表合并成一個新的有序表,即把待排序序列分為若干個子序列,每個子序列是有序的。然後再把有序子序列合并為整體有序序列。
歸併排序是建立在歸併操作上的一種有效排序演算法。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。 將已有序的子序列合并,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合并成一個有序表,稱為2-路歸併。
歸併排序的時間複雜度是O(N*lgN)。
2、工作原理
歸併排序(Merge Sort)就是利用歸併思想對數列進行排序。根據具體的實現,歸併排序包括”從上往下”和”從下往上”2種方式。
下面的圖片很清晰的反映了”從下往上”和”從上往下”的歸併排序的區別。
從下往上的歸併排序:將待排序的數列分成若干個長度為1的子數列,然後將這些數列兩兩合并,得到若干個長度為2的有序數列,再將這些數列兩兩合并,得到若干個長度為4的有序數列,再將它們兩兩合并,直接合并成一個數列為止。這樣就得到了我們想要的排序結果。
從上往下的歸併排序:它與”從下往上”在排序上是反方向的。它基本包括3步: 分解:將當前區間一分為二,即求分裂點 mid = (low + high)/2; 求解:遞迴地對兩個子區間a[low…mid] 和 a[mid+1…high]進行歸併排序。遞迴的終結條件是子區間長度為1。 合并:將已排序的兩個子區間a[low…mid]和 a[mid+1…high]歸併為一個有序的區間a[low…high]。
3、Java實現
/** * @Comment 歸併排序演算法 * @Author Ron * @Date 2017年10月26日 下午2:56:33 * @return */public class MergeSort { /** * @Comment 合并子序列 * @Author Ron * @Date 2017年10月26日 下午5:08:11 * @param sources 待排序的數組 * @param start 第一個子序列起始位置 * @param mid 第一個子序列結束位置,第二個子序列起始位置 * @param end 第二個子序列結束位置 * @return */ static void merge(int[] sources,int start,int mid,int end){ int[] temp = new int[end-start+1];//匯總兩個序列的臨時地區 int i = start; int j = mid+1; int k=0; while (i <= mid && j <= end) { if(sources[i] <= sources[j]){ temp[k++]=sources[i++]; }else{ temp[k++]=sources[j++]; } } while (i <= mid) { temp[k++]=sources[i++]; } while (j <= end) { temp[k++]=sources[j++]; } //將temp賦給sources for (i = 0; i < k; i++){ sources[start + i] = temp[i]; } } /** * @Comment * @Author Ron * @Date 2017年10月26日 下午4:28:04 * @param sources 待排序的數組 * @param gap 子序列長度 * @return */ static void mergeGroup(int[] sources,int gap){ int margedLength = 0;//已歸併的序列起始下標 int length = sources.length; for(margedLength = 0 ; margedLength + gap*2 - 1 < length; margedLength += gap*2){ //合并兩個子序列 merge(sources, margedLength, margedLength+gap-1, margedLength+2*gap-1); } if (margedLength + gap - 1 < length) { //合并剩下的子序列 merge(sources, margedLength, margedLength+gap-1, length-1); } } /** * @Comment 歸併排序(從上到下) * @Author Ron * @Date 2017年10月26日 下午3:42:12 * @return */ static void mergeSortOne(int[] sources){ if(sources == null){ return; } //分組合并 for(int gap=1; gap < sources.length; gap *= 2){ mergeGroup(sources,gap); } } /** * @Comment 二分歸併 * @Author Ron * @Date 2017年10月26日 下午5:58:42 * @param sources 需要排序的序列 * @param start 序列起始位置 * @param end 序列結束位置 * @return */ static void mergeDivide(int[] sources,int start,int end){ if(sources==null || start >= end){ //子區間長度為1,返回 return; } int mid = (start+end)/2; mergeDivide(sources,start,mid); mergeDivide(sources,mid+1,end); merge(sources, start, mid, end); } public static void main(String[] args) { int[] a = {80,30,60,40,20,10,50,70,90}; mergeSortOne(a); for (int i=0; i<a.length; i++){ System.out.printf("%d ", a[i]); } System.out.printf("\n"); int[] a2 = {80,30,60,40,20,10,50,70,90}; mergeDivide(a2,0,a2.length-1); for (int i=0; i<a2.length; i++){ System.out.printf("%d ", a2[i]); } }}