歸併排序編程
歸併排序基本的操做是合併兩個已排序的數組,以下面的例子:數組
A:{1,2,4,7}ide
B:{2,2,5,9}spa
第一步:3d
比較A[0]和B[0],A[0]<B[0],將A[0]複製到C[0]中,獲得code
C{0}blog
第二步:排序
比較A[1]和B[0],A[1]=B[0],將A[1]複製到C[0]中,獲得遞歸
C{1,2}。it
循環以上步驟,即獲得排序後的序列:
C{1,2,2,2,4,5,7,9}。
這就是歸併排序的基本原理,那麼咱們能夠先給出合併兩個已經有序的數組的代碼:
public void merge(Integer[]a,Integer low,Integer mid,Integer high) { Integer i = low; /* 這裏爲何不能用mid,由於以前在遞歸時是以mid+1分割的: [1,2,8,3,4] low=0 mid=2 high=4 i=0 j=2 [1] i=1 j=2 [1,2] i=2 j=2 [1,2,8,3,4]*/ Integer j = mid + 1; Integer[] b = new Integer[high + 1]; for(int k=low;k<=high;k++) { b[k] = a[k]; } print("b",b); for(int k=low;k<=high;k++) { //第一個有序子數組已經遍歷完 if(i > mid) a[k] = b[j++]; //第二個有序子數組已經遍歷完 else if(j > high) a[k] = b[i++]; else if(b[i] < b[j]) a[k] = b[i++]; else a[k] = b[j++]; } }
這段代碼實現了合併兩個已經有序的數組到一個新數組。只不過在這裏咱們使用一個數組代替了兩個有序數組,以下:
A’[1,2,4,7,2,2,5,9] 是把上面提到的兩個有序數組A和B放在一個數組A’中,用mid=3來分割。
最後在代碼中新建一個數組B[],將A’中的元素複製到B。
再用循環依次將元素排序放回A中。
可是如今這段代碼只能將兩個已經有序的數組,歸併後到一個新數組,讓這個新數組編程有序的。
若是是一個無序的數組呢?
這裏就須要用到一下的思想:
咱們能夠先將一個無序數組A,按照2位單位,分紅諸多長度爲2的子數組:
以下:
假若有數組A:[2 ,1 ,5 ,9 ,0 ,6 ,8 ,7 ,3]
能夠分紅如下長度爲1的子數組:
{2}、{1}、{5}、{9}、{0}、{6}、{8}、{7}、{3}
那麼對這9個子數組進行歸併排序,也即便用上面提到的代碼進行排序,那麼就能夠獲得
{1,2}、{5,9}、{0,6}、{7,8}、{3}
這樣咱們就有5個有序的子數組了,再講這五個子數組兩兩歸併,即獲得:
{1,2,5,9}、{0,6,7,8}、{3}
就這樣依次歸併下去,即獲得一個有序的數組B
{0 ,1 ,2 ,3 ,5 ,6 ,7 ,8 ,9}
在這裏,很明顯能感受到一絲遞歸的意味,那麼先直接給出代碼:
//遞歸實現,自頂向下 public void mergeSort(Integer[] a,Integer low,Integer high) { if(low >= high) return; Integer mid = (low + high)/2; mergeSort(a,low,mid); mergeSort(a,mid+1,high); merge(a,low,mid,high); }
相信若是理解了以上所說的遞歸排序的原理,這段代碼應該很是好懂,用遞歸的好處就是邏輯簡單,符合人的直觀思惟,只須要將一個待排序的數組依次分紅2,4,8...等若干個數組,直到獲得A.length個長度爲1的子數組,依次歸併後獲得若干個長度爲2的有序子數組,再進行歸併,獲得若干個長度爲4的子數組(固然,有可能最後一個子數組長度不必定恰好爲2或4,即數組的長度不必定爲2的倍數)。
這樣一直歸併下去,即獲得有序的數組。
這是使用遞歸來實現,那麼應該還有一種不使用遞歸的實現,以下:
//非遞歸,自底向上 public void mergeSortNonRecursion(Integer[] a) { //第一層循環 表示歸併排序子數組的長度 從1 , 2 , 4 ,8 ..... for(int i=1;i<a.length;i *= 2) { //第二層循環表示每兩個自數組之間歸併排序,肯定起始和終止INDEX for(int low=0;low<a.length;low += 2*i) { merge(a, low, low + i- 1, Math.min(low + 2*i - 1, a.length - 1)); } } }
第一層循環表示歸併的次數,
第一次分紅n個長度爲1的子數組,進行歸併
第二次分紅n/2個長度爲2的子數組....
結論就是,一個長度爲n的數組須要歸併logn次。
第二層循環表示把兩個子數組進行歸併
效率:歸併排序的時間複雜度爲NlogN
完整代碼以下:
public class MergeSort extends SortBase { @Override public Integer[] sort(Integer[] a) { // TODO Auto-generated method stub print("init",a); //mergeSort(a,0,a.length-1); mergeSortNonRecursion(a); print("result",a); return a; } //遞歸實現,自頂向下 public void mergeSort(Integer[] a,Integer low,Integer high) { if(low >= high) return; Integer mid = (low + high)/2; mergeSort(a,low,mid); mergeSort(a,mid+1,high); merge(a,low,mid,high); } public void merge(Integer[]a,Integer low,Integer mid,Integer high) { Integer i = low; /* 這裏爲何不能用mid,由於以前在遞歸時是以mid+1分割的: [1,2,8,3,4] low=0 mid=2 high=4 i=0 j=2 [1] i=1 j=2 [1,2] i=2 j=2 [1,2,8,3,4]*/ Integer j = mid + 1; Integer[] b = new Integer[high + 1]; for(int k=low;k<=high;k++) { b[k] = a[k]; } print("b",b); for(int k=low;k<=high;k++) { //第一個有序子數組已經遍歷完 if(i > mid) a[k] = b[j++]; //第二個有序子數組已經遍歷完 else if(j > high) a[k] = b[i++]; else if(b[i] < b[j]) a[k] = b[i++]; else a[k] = b[j++]; } } //非遞歸,自底向上 public void mergeSortNonRecursion(Integer[] a) { //第一層循環 表示歸併排序子數組的長度 從1 , 2 , 4 ,8 ..... for(int i=1;i<a.length;i *= 2) { //第二層循環表示每兩個自數組之間歸併排序,肯定起始和終止INDEX for(int low=0;low<a.length;low += 2*i) { merge(a, low, low + i- 1, Math.min(low + 2*i - 1, a.length - 1)); } } } public static void main(String[] args) { Integer[] a = {2,1,5,9,0,6,8,7,3}; (new MergeSort()).sort(a); } }
運行結果以下:
init: [2 ,1 ,5 ,9 ,0 ,6 ,8 ,7 ,3]
歸併2和1
b: [2 ,1]
歸併5和9,即依次歸併兩個長度爲1的子數組,獲得長度爲2的有序子數組
b: [null ,null ,5 ,9]
b: [null ,null ,null ,null ,0 ,6]
b: [null ,null ,null ,null ,null ,null ,8 ,7]
b: [null ,null ,null ,null ,null ,null ,null ,null ,3]
歸併一、二、五、9,即依次歸併兩個長度爲2的子數組,獲得長度爲4的有序子數組
b: [1 ,2 ,5 ,9]
b: [null ,null ,null ,null ,0 ,6 ,7 ,8]
b: [null ,null ,null ,null ,null ,null ,null ,null ,3]
歸併1 ,2 ,5 ,9 ,0 ,6 ,7 ,8,即依次歸併兩個長度爲4的子數組,獲得長度爲8的有序子數組
b: [1 ,2 ,5 ,9 ,0 ,6 ,7 ,8]
b: [null ,null ,null ,null ,null ,null ,null ,null ,3]
b: [0 ,1 ,2 ,5 ,6 ,7 ,8 ,9 ,3]
result: [0 ,1 ,2 ,3 ,5 ,6 ,7 ,8 ,9]
從結果上看,能夠很清晰的看出來是兩兩歸併。