歸併排序就是利用歸併的思想實現的排序方法。他的原理是假設初始序列含有n個記錄,則可以看成是n個有序的子序列,每個子序列的長度爲1,然後兩兩歸併,得到[n/2] (整數)個長度爲2或1的有序子序列;再兩兩歸併,......,如此重複,直至得到一個長度爲n的有序序列爲止,這種排序方法稱爲2路歸併排序。
先來看代碼:
void MergeSort(Sqlist *L)
{
MSort(L->r,L->r,1,L->length);
}
只是調用了一個函數,封裝了一下,假設現在要對數組{50,10,90,30,70,40,80,60,20}進行排序,L.length = 9,則來看看MSort()函數的實現:
void MSort(int SR[],int TR1[],int s,int t)
{
int m;
int TR2[MAXSIZE + 1];
if(s == t)
TR1[s] = SR[s];
else
{
m = (s + t)/2;
MSort(SR,TR2,s,m);
MSort(SR,TR2,m+1,t);
Merge(TR2,TR1,s,m,t);
}
}
1.MSort()bei'被調用時,SR與TR1都是{50,10,90,30,70,40,80,60,20},s = 1,t = 9。
2.第5行 顯然s不等於t,執行第8~13行語句塊。
3.第9行,m = (1+9)/2 = 5,m就是序列的正中間的下標。
4.此時第10行,調用MSort(SR,TR2,1,5)的目標就是將數組SR中的第1~5的關鍵字歸併到有序的TR2(調用前TR2爲空數組),第11行調用MSort(SR,TR2,6,9)的目標就是將數組SR中的第6~9的關鍵字歸併到有序的TR2.也就是說,在調用這兩句代碼之前,代碼已經準備將數組分成了兩組了。
5.第12行就是分別將數組變得有序。
6.最後遞歸進去之後總的來說就是按照下圖進行遞歸返回(圖來自大話數據結構):
再來看一下Merge函數的代碼:
/*將有序的SR[i..m]和SR[m+1..n]歸併爲有序的TR[i..n]*/
void Merge(int SR[],int TR[],int i,int m,int n)
{
int j,k,l;
for(j = m +1,k = i;i <= m && j <= n;k++) //將SR中記錄由小到大歸併到TR
{
if(SR[i] < SR[j])
TR[k] = SR[i++];
else
TR[k] = SR[j++];
}
if(i <= m)
{
for(l = 0;l <= m - i;l++)
TR[k + l] = SR[i + l]; //將剩餘的SR[i..m]複製到TR
}
if(j <= n)
{
for(l = 0;l <= n - j;l++)
TR[k + l] = SR[j + l]; //將剩餘的SR[j..n]複製到TR
}
}
複雜度分析:
時間複雜度爲O(nlogn);
空間複雜度爲O(n + logn);
非遞歸實現歸併排序
直接看代碼:
/*對順序表L作歸併非遞歸排序*/
void MergeSort2(Sqlist *L)
{
int *TR = (int *)malloc(L->length*sizeof(int)); //申請額外空間
int k = 1;
while(k < L->length)
{
MergePass(L->r,TR,k,L->length);
k = 2*k; //子序列長度加倍
MergePass(TR,L->r,k,L->length);
k = 2*k; //子序列長度加倍
}
}
MergePass()函數的實現:
/*將SR[]中相鄰長度爲s的子序列兩兩歸併到TR[]中*/
void MergePass(int SR[],int TR[],int s,int n)
{
int i = 1;
int j;
while(i <= n - 2*s + 1)
{
Merge(SR,TR,i,i+s-1,i+2*s-1); //兩兩歸併
i = i + 2*s;
}
if(i < n-s+1) //歸併最後兩個序列
Merge(SR,TR,i,i+s-1,n);
else //若最後只剩下單個子序列
for(j = i;j <= n;j++)
TR[j] = SR[j];
}