歸併排序(Merge Sort)就是利用歸併的思想實現的排序方法。它的原理是假設初始序列含有fn個記錄,則能夠當作是n個有序的子序列,每一個子序列的長度爲1,而後兩兩歸併,獲得[n2\frac{n}{2}2n]([x]表示不小於x的最小整數)個長度爲2或1的有序子序列;在兩兩歸併,…,如此重複,知道獲得一個長度爲n的有序序列爲止,這種排序方法稱爲2路歸併排序。html
根據具體的實現,歸併排序包括"從上往下"和"從下往上"2種方式。
下面的圖片很清晰的反映了"從下往上"和"從上往下"的歸併排序的區別。
java
代碼實現:算法
/** * 從上到下 * @param elem * @param start * @param end */ public void mergeSortUp2Down(int[] elem, int start, int end) { if(elem == null || start >= end) { return; } int mid = (start + end) / 2; mergeSortUp2Down(elem, start, mid); mergeSortUp2Down(elem, mid + 1, end); merge(elem, start, mid, end); } public void merge(int[] elem, 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(elem[i] < elem[j]) { temp[k++] = elem[i++]; } else { temp[k++] = elem[j++]; } } while(i <= mid) { temp[k++] = elem[i++]; } while(j <= end) { temp[k++] = elem[j++]; } for (i = 0; i < k; i++) { elem[start + i] = temp[i]; } temp = null; }
從上往下的思路如圖所示:
數組
代碼實現:數據結構
/** * 從下到上 * @param elem */ public void mergeSortDown2Up(int[] elem) { if(elem == null) return; for (int i = 1; i < elem.length; i *= 2) { mergeGroups(elem, elem.length, i); } } public void mergeGroups(int[] elem, int len, int gap) { int i; for (i = 0; i + 2 * gap -1 < len; i += (2 * gap)) { merge(elem, i, i + gap -1, i + 2 * gap -1); } if(i + gap -1 < len - 1) { merge(elem, i, i + gap - 1, len - 1); } }
歸併排序的時間複雜度是O(nlog\loglogn)。
假設被排序的數列中有n個元素。遍歷一趟的時間複雜度是O(n),須要遍歷多少次呢?歸併排序的形式就是一棵二叉樹,它須要遍歷的次數就是二叉樹的深度,而根據徹底二叉樹的性質能夠得知能夠得出它的時間複雜度是O(nlog\loglogn)。spa
因爲歸併怕徐在歸併過過程當中須要與原始記錄序列一樣數量的存儲空間存放歸併結果以及遞歸時深度爲log2log_2log2n的棧空間,因此空間複雜度爲O(n + log\loglogn)指針
歸併排序是穩定的算法,它知足穩定算法的定義。code
非遞歸的思想和遞歸同樣,均爲先分解後合併,非遞歸的重點在於如何肯定併合理的分解待排序數組。
對於非遞歸來說,切分的不向遞歸從大到小,非遞歸實際上從一開始構建算法的時候都從小到大。
第一次切分排序就肯定最小單位爲1個數字,將2個數字組合爲一組。
第二次切分排序肯定爲2個數字,將4個數字組合爲一組。
第三次切分排序肯定爲4個數字,將8(7)個數字組合爲一組。
也就是說非遞歸歸併排序中分解的依據爲:從切分的長度爲1開始,一次歸併變回原來的2倍。每完成一次歸併則 gap = gap * 2。htm
/** * 非遞歸 * @param elem */ public void mergeSortNon(int[] elem) { int gap = 1; while(gap <= elem.length) { for (int i = 0; i + gap < elem.length; i += (gap * 2)) { int start = i, mid = i + gap -1, end = i + 2 * gap -1; if(end > elem.length - 1) { end = elem.length - 1; } merge(elem, start, mid, end); } gap *= 2; } }
完整代碼:blog
public class Test { /** * 從上到下 * @param elem * @param start * @param end */ public void mergeSortUp2Down(int[] elem, int start, int end) { if(elem == null || start >= end) { return; } int mid = (start + end) / 2; mergeSortUp2Down(elem, start, mid); mergeSortUp2Down(elem, mid + 1, end); merge(elem, start, mid, end); } /** * 從下到上 * @param elem */ public void mergeSortDown2Up(int[] elem) { if(elem == null) return; for (int i = 1; i < elem.length; i *= 2) { mergeGroups(elem, elem.length, i); } } public void mergeGroups(int[] elem, int len, int gap) { int i; for (i = 0; i + 2 * gap -1 < len; i += (2 * gap)) { merge(elem, i, i + gap -1, i + 2 * gap -1); } if(i + gap -1 < len - 1) { merge(elem, i, i + gap - 1, len - 1); } } /** * 非遞歸 * @param elem */ public void mergeSortNon(int[] elem) { int gap = 1; while(gap <= elem.length) { for (int i = 0; i + gap < elem.length; i += (gap * 2)) { int start = i, mid = i + gap -1, end = i + 2 * gap -1; if(end > elem.length - 1) { end = elem.length - 1; } merge(elem, start, mid, end); } gap *= 2; } } public void merge(int[] elem, 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(elem[i] < elem[j]) { temp[k++] = elem[i++]; } else { temp[k++] = elem[j++]; } } while(i <= mid) { temp[k++] = elem[i++]; } while(j <= end) { temp[k++] = elem[j++]; } for (i = 0; i < k; i++) { elem[start + i] = temp[i]; } temp = null; } public static void main(String[] args) { Test t = new Test(); int[] elem = {80,30,60,40,20,10,50,70}; t.mergeSortUp2Down(elem, 0, elem.length - 1); //從上到下 // t.mergeSortDown2Up(elem); //從下到上 // t.mergeSortNon(elem); //非遞歸 for (int i = 0; i < elem.length; i++) { System.out.print(elem[i] + ", "); } } }
參考:
http://www.javashuo.com/article/p-mcvhjvfq-bv.html
https://www.cnblogs.com/yulinfeng/p/7078661.html?utm_source=itdadao&utm_medium=referral
《大話數據結構》
最近在作一道《劍指Offer》上的一道算法的時候,用到了歸併排序,所以加深了我對歸併排序的核心排序(merge())方法加深了理解:
其實核心的排序就是設立三個指針P1, P2, P3。P3指針所指的數組就是兩個數組合而且排序後的數組。每次比較P1和P2指針所指的元素: