学了几天的排序,我们也该把注意力转移到归并排序身上了,归并排序以O(N*logN)最坏情形运行时间运行,而其所用的比较次数几乎是最优的。
归并排序采用了最基本的分治(divide-and-conquer)策略,它将问题分成一些小的问题然后递归求解,再将各个阶段的答案修补到一起,首先我们来看一下归并排序最基本的归并过程,递归是依赖于这个归并过程的,代码如下:
void Merge(int a[],int tmp[],int Lpos,int Rpos,int RightEnd)//归并的过程
{
int i,leftend,num,tmpos;
leftend=Rpos-1;
tmpos=Lpos;//暂时的位置,从左开始
num=RightEnd-Lpos+1;//要排序的数目
while(Lpos<=leftend&&Rpos<=RightEnd)
{
if(a[Lpos]<=a[Rpos])
tmp[tmpos++]=a[Lpos++];
else
tmp[tmpos++]=a[Rpos++];
}
while(Lpos<=leftend)
tmp[tmpos++]=a[Lpos++];
while(Rpos<=RightEnd)
tmp[tmpos++]=a[Rpos++];
for(i=0;i<num;i++,RightEnd--)
a[RightEnd]=tmp[RightEnd];//现在整个tmp都是有序的,把值全部赋给a
}
Merge过程把每次排序好的先放到一个临时数组中,最后再把该数组的值赋给主数组。
下面就是归并排序的递归过程了,它把问题分成两大块,左半部分和右半部分,再递归排序,最后合并,代码如下:
void Msort(int a[],int tmp[],int left,int right)
{
int center;//中间的位置
if(left<right)
{
center=(left+right)/2;
Msort(a,tmp,left,center);//先排前半部分
Msort(a,tmp,center+1,right);//再排后半部分
Merge(a,tmp,left,center+1,right);//前后两部分合并
}
}
最后就是归并排序的实现了,我们在该函数里定义了一个临时数组。
void MergeSort(int a[],int n)
{
int *tmp;
tmp=(int*)malloc(sizeof(int)*n);
Msort(a,tmp,0,n-1);//调用归并排序的递归例程
}
虽然归并排序的运行时间是O(N*logN),但是它很难应用于主存排序,主要问题在于合并两个排序的表需要线性附加内存,在整个算法中还要花费将数据拷贝到临时数组再拷贝回来这样一个附加工作,其结果就放慢了排序的速度。