归并排序有两种方式:
(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)。