(2) 二路归并排序的算法演示
【参见动画演示】
(3) 一趟归并算法
分析:
在某趟归并中,设各子文件长度为length(最后一个子文件的长度可能小于length),则归并前R[1..n]中共有个有序的子文件:
R[1..length],[length+1..2length],…, 。
注意:
调用归并操作将相邻的一对子文件进行归并时,必须对子文件的个数可能是奇数、以及最后一个子文件的长度小于length这两种特殊情况进行特殊处理:
① 若子文件个数为奇数,则最后一个子文件无须和其它子文件归并(即本趟轮空);
② 若子文件个数为偶数,则要注意最后一对子文件中后一子文件的区间上界是n。
具体实现算法:
[cpp]
void MergePass(SeqList R,int length)
{ //对R[1..n]做一趟归并排序
int i;
for(i=1;i+2*length-1<=n;i=i+2*length)
Merge(R,i,i+length-1,i+2*length-1);
//归并长度为length的两个相邻子文件
if(i+length-1 Merge(R,i,i+length-1,n); //归并最后两个子文件
//注意:若i≤n且i+length-1≥n时,则剩余一个子文件轮空,无须归并
} //MergePass
void MergePass(SeqList R,int length)
{ //对R[1..n]做一趟归并排序
int i;
for(i=1;i+2*length-1<=n;i=i+2*length)
Merge(R,i,i+length-1,i+2*length-1);
//归并长度为length的两个相邻子文件
if(i+length-1 Merge(R,i,i+length-1,n); //归并最后两个子文件
//注意:若i≤n且i+length-1≥n时,则剩余一个子文件轮空,无须归并
} //MergePass
(4)二路归并排序算法
[cpp] view plaincopyprint void MergeSort(SeqList R)
{//采用自底向上的方法,对R[1..n]进行二路归并排序
int length;
for(1ength=1;length MergePass(R,length); //有序段长度≥n时终止
}
void MergeSort(SeqList R)
{//采用自底向上的方法,对R[1..n]进行二路归并排序
int length;
for(1ength=1;length MergePass(R,length); //有序段长度≥n时终止
}
注意:
自底向上的归并排序算法虽然效率较高,但可读性较差。
自顶向下的方法
采用分治法进行自顶向下的算法设计,形式更为简洁。
(1)分治法的三个步骤
设归并排序的当前区间是R[low..high],分治法的三个步骤是:
①分解:将当前区间一分为二,即求分裂点
②求解:递归地对两个子区间R[low..mid]和R[mid+1..high]进行归并排序;
③组合:将已排序的两个子区间R[low..mid]和R[mid+1..high]归并为一个有序的区间R[low..high]。
递归的终结条件:子区间长度为1(一个记录自然有序)。