歸併:將兩個有序的數組歸併成一個更大的有序數組。java
歸併排序(分治思想):要將一個數組排序,能夠先(遞歸地)將它分別分紅兩半分別排序,而後將結果歸併起來。算法
優勢:它可以保證將任意長度爲N的數組排序所需時間和NlogN成正比,相對於初級排序,時間大大縮短;數組
缺點:它所須要的額外空間和N成正比。less
原地歸併的實現:先將全部元素複製到另外一額數組(aux[])中,而後在歸併回原數組(a[])中,具體歸併是循環比較兩個有序數組的最左側元素即最小值,哪一個更小,即將這個元素複製回原數組(a[])。性能
實現代碼以下:測試
public static void merge(Comparable[] a,int lo,int mid,int hi) { //將a[lo..mid]和a[mid+1..hi]歸併 int i=lo,j=mid+1; for(int k=lo;k<=hi;k++) {//將數組a複製到數組aux aux[k]=a[k]; } for(int k=lo;k<=hi;k++)//遍歷整個aux,歸併回數組a if(i>mid) a[k]=aux[j++];//若是左側數組遍歷完成,直接讀取右側數組 else if(j>hi)a[k]=aux[i++];//若是右側數組遍歷完成,直接讀取左側數組 else if(less(aux[j],aux[i]))a[k]=aux[j++];//比較兩個數組最左側的值,誰小歸併誰 else a[k]=aux[i++]; }
自頂向下的歸併排序:spa
public class Merge{ private static Comparable[] aux;//歸併所需的輔助數組 public static void sort(Comparable[] a) { aux=new Comparable[a.length];//一次性分配空間 sort(a,0,a.length-1); } private static void sort(Comparable[] a,int lo,int hi) { //將數組a[lo..hi]排序 if(hi<=lo)return; int mid=lo+(hi-lo)/2; sort(a,lo,mid);//將左半邊排序 sort(a,mid+1,hi);//將右半邊排序 merge(a,lo,mid,hi); } }
性能:對於長度爲N的任意數組,自頂向下的歸併須要1/2NlgN至NlgN次比較;最多須要訪問數組6NlgN。code
改進:①對小規模子數組使用插入排序——是用插入排序處理小規模的子數組(好比長度小於15)通常能夠將歸併排序的巡行時間縮短10%~15%;排序
②測試數組是否已經有序——添加判斷條件,若是a[mid]小於等於a[mid+1],咱們就認爲數組已經有序而不須要在進行歸併即不須要在調用merge()方法,所以能夠將任意有序的子數組算法運行時間變爲線性;遞歸
③不將元素複製到輔助數組——能夠節省複製到用於歸併的輔助數組所用的時間(但空間不能夠)。一是經過將數據從輸入數組排序到輔助數組;二是將數據從輔助數組排序到輸入數組。
自底向上的歸併方法:
public class MergeBU{ private static Comparable[] aux;//歸併所需的輔助數組 public static void sort(Comparable[] a) { //進行lgN次兩兩歸併 int N=a.length; aux=new Comparable[N]; for(int sz=1;sz<N;sz=sz+sz)//sz子數組大小 for(int lo=0;lo<N-sz;lo+=sz+sz)//lo:子數組索引 merge(a,lo,lo+sz-1,Math.min(lo+sz+sz-1, N-1)); } }
自底向上的歸併排序會屢次遍歷整個數組,根據子數組大小進行兩兩歸併。子數組的大小sz的初始值爲1,每次加倍。最後一個子數組的大小隻有在數組大小是sz的偶數倍的時候纔會等於sz(不然它會比sz小)。
性能:對於長度爲N的任意數組,自底向上的歸併排序須要1/2NlgN至NlgN次比較,最多訪問數組6NlgN次。
當數組長度爲2的冪時,自頂向下和自底向上的歸併排序所用的比較次數和數組訪問次數相同,只是順序不一樣。
自底向上的歸併排序比較適合用鏈表組織的數據。
沒有任何基於比較的算法可以保證使用少於lg(N!)~NlgN次比較將長度爲N的數組排序。
歸併排序是一種漸進最優的基於比較排序的算法。