歸併排序(Merge Sort)是利用”歸併”技術來進行排序。歸併是指將若干個已排序的子文件合併成一個有序的文件。
歸併排序有兩種實現方式:1、自底向上 2、自頂向下。
1、自底向上的基本思想:
第1趟歸併排序時,將待排序的文件R[1..n]看作是n個長度爲1的有序子文件,將這些子文件兩兩歸併,若n爲偶數,則得到 個長度爲2的有序子文件;若n爲奇數,則最後一個子文件不參與歸併。故本趟歸併完成後,前 logn個有序子文件長度爲2,n位奇數的話,最後一個子文件長度爲1;第2趟歸併則是將第1趟歸併所得到的 logn個有序的子文件兩兩歸併,如此反覆,直到最後得到一個長度爲n的有序文件爲止。
實現代碼:
function merge(a,b,low,m,high){
var i,j,k,t;
i=low;
j=m+1;
k=low;
while(i<=m&&j<=high){
b[k++]=(a[i]<=a[j])?a[i++]:a[j++];
}
if(i<=m){
for(t=i;t<=m;t++){
b[k+t-i]=a[t];
}
}else{
for(t=j;t<=high;t++){
b[k+t-j]=a[t];
}
}
}
function mergepass(a,b,len){
var i,j,n=a.length;
i=0;
while(i<n-2*len+1){
merge(a,b,i,i+len-1,i+2*len-1);
i=i+2*len;
}
if(i+len-1<n) { //尚有兩個子文件,其中後一個長度小於length
//例如[1,4,5,9],[7,11,13]是最後兩個合併的子文件,但是最後一個文件的長度小於4,合併最後這兩個文件
merge(a, b, i, i + len - 1, n - 1); //將最後兩個子文件夾合併
}
}
2、自頂向下方法的基本思想
採用分治法進行自頂向下的算法設計,形式更爲簡潔。
(1)分治法的三個步驟
設歸併排序的當前區間是R[low..high],分治法的三個步驟是:
①分解:將當前區間一分爲二,像二分法。 mid=(left+right)/2;
②求解:遞歸地對兩個子區間R[low..mid]和R[mid+1..high]進行歸併排序;
③組合:將已排序的兩個子區間R[low..mid]和R[mid+1..high]歸併爲一個有序的區間R[low..high]。
遞歸的終結條件:子區間長度爲1(一個記錄自然有序)。
function MergeSortDC(a,b,low,high)
{//用分治法對R[low..high]進行二路歸併排序
var mid
if(low<high){//區間長度大於1
mid=Math.floor((low+high)/2) //分解
MergeSortDC(a,b,low,mid); //遞歸地對R[low..mid]排序
MergeSortDC(a,b,mid+1,high) //遞歸地對R[mid+1..high]排序
mergeTop(a,b,low,mid,high) //組合,將兩個有序區歸併爲一個有序區
}
}
function mergeTop(a,b,low,m,high){
var i=low,j=m+1,p=0; //置初始值
while(i<=m&&j<=high) //兩子文件非空時取其小者輸出到R1[p]上
b[p++]=(a[i]<=a[j])?a[i++]:a[j++];
while(i<=m) //若第1個子文件非空,則複製剩餘記錄到R1中
b[p++]=a[i++];
while(j<=high) //若第2個子文件非空,則複製剩餘記錄到R1中
b[p++]=a[j++]
for(p=0,i=low;i<=high;p++,i++)
a[i]=b[p]//歸併完成後將結果複製回R[low..high]
}
二、算法分析
1、穩定性
歸併排序是一種穩定的排序。
2、存儲結構要求
可用順序存儲結構。也易於在鏈表上實現。
3、時間複雜度
對長度爲n的文件,需進行 趟二路歸併,每趟歸併的時間爲O(n),故其時間複雜度無論是在最好情況下還是在最壞情況下均是O(nlgn)。
4、空間複雜度
需要一個輔助向量來暫存兩有序子文件歸併的結果,故其輔助空間複雜度爲O(n),顯然它不是就地排序。