歸併排序有兩種方式:
(1): 自底向上的方法
自底向上的基本思想是:第1趟歸併排序時,將待排序的文件R[1..n]看作是n個長度爲1的有序子文件,將這些子文件兩兩歸併,若n爲偶數,則得到n/2個長度爲2的有序子文件;若n爲奇數,則最後一個子文件輪空(不參與歸併)。故本趟歸併完成後,前n/2 - 1個有序子文件長度爲2,但最後一個子文件長度仍爲1;第2趟歸併則是將第1趟歸併所得到的n/2個有序的子文件兩兩歸併,如此反覆,直到最後得到一個長度爲n的有序文件爲止。
初始化:{43,23,54,44,2,7,19}
第一趟:{23,43,44,54,2,7,19}
第二趟:{23,43,44,54,2,7,19}
第三趟:{2,7,19,23,43,44,54}
第一趟 【43,23】【54,44】【2,7】【19】
第二趟 【23,43,44,54】【2,7,19】
第三趟 【2,7,19,23,43,44,54】
// 歸併排序
public void Merge(List<int> seqlist,int len)
{
int m = 0; //臨時順序表的起始位置
int l1 = 0;//第1個有序表的起始位置
int h1; //第1個有序表的結束位置
int l2; //第2個有序表的起始位置
int h2; //第2個有序表的結束位置
int i = 0; //臨時表示第一個有序表的起始位置
int j = 0; //臨時表示第二個有序表的起始位置
// 臨時表,用於臨時將2個有序表合併爲一個有序表
List<int> tmp = new List<int>();
for (int q = 0; q < seqlist.Count; q++)
{
tmp.Add(0);// 給list添加值
}
// 歸併處理
while (l1+len<seqlist.Count)
{
l2 = l1 + len;//第二個有序表的開始位置
h1 = l2 - 1; //第一個有序表的結束位置
// 第二個有序表的結束位置
h2 = (l2 + len - 1 < seqlist.Count) ? l2 + len - 1 : seqlist.Count - 1;
j = l2; //第二個有序表的起始位置賦給j
i = l1; //第一個有序表的起始位置賦給i
// 兩個有序表中的記錄沒有排序完
while ((i<=h1)&&(j<=h2))
{
//第一個有序表記錄的關鍵碼小於第二個有序表記錄的關鍵碼
if (seqlist[i]<=seqlist[j])
{
tmp[m++] = seqlist[i++];
}
else
{
tmp[m++] = seqlist[j++];
}
}
// 第一個有序表中還有記錄沒有排序完
while (i<=h1)
{
tmp[m++] = seqlist[i++];
}
// 第二個有序表中還有記錄沒有排序完
while (j<=h2)
{
tmp[m++] = seqlist[j++];
}
l1 = h2 + 1;
}
i = l1;
// 原順序表中還有記錄沒有排序完
while (i<seqlist.Count)
{
tmp[m++] = seqlist[i++];
}
// 臨時順序表中的記錄複製到順序表,使原順序表中的記錄有序
for (i = 0; i < seqlist.Count; i++)
{
seqlist[i] = tmp[i];
}
}
// 二路歸併排序算法
public void MergeSort(List<int> seqlist)
{
int n = 1;// 歸併增量
while (n < seqlist.Count)
{
Merge(seqlist,n);
n *= 2;
}
}
對於n個記錄的順序表,將這n個記錄看作葉子結點,若將兩兩歸併生成的子
表看作它們的父結點,則歸併過程對應於由葉子結點向根結點生成一棵二叉樹的
過程。所以,歸併趟數約等於二叉樹的高度減1,即log2n,每趟歸併排序記錄關
鍵碼比較的次數都約爲n/2,記錄移動的次數爲2n(臨時順序表的記錄複製到原
順序表中記錄的移動次數爲n)。因此,二路歸併排序的時間複雜度爲O(nlog2n)。
而二路歸併排序使用了n個臨時內存單元存放記錄,所以,二路歸併排序算法的
空間複雜度爲O(n)。