一.相关概念
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
线性表在合并时用的是有序线性表快速合并的算法,即要合并两个有序递减线性表La,Lb,则需创建一个新的线性表Lc,循环遍历La,Lb中的元素,哪个元素更小就先存入Lc中.相必大家都知道这个简单算法,不知道的可以看我的之前一篇博客:线性表求并集
线性表在分解时简单的算法用的就是递归,但是递归算法实用性很差,数据量大一些很容易就爆栈,所以我要在这里教大家非递归二路归并排序算法的实现
二.思路分析
1.首先如何分解?我们用非递归实现就是依次进行1 1 合并,2 2合并, 4 4 合并, 8 8 合并,16 16 合并…所以我们用一个循环就可以实现(d*=2),第一趟以2个元素为一对要合并的线性表(d=2,一个线性表1个元素),第二趟4个一对(d=4,一个线性表2个元素),.第三趟8个一对(d=8,一个线性表4个元素),每趟过程中对每对进行有序线性表快速合并(每对看作是有两个线性表)
2.既然是进行有序线性表快速合并,则需要创建一个和原线性表L同等大小的线性表list
第一趟2个一对,每对内进行合并,也就是1 1 合并,合并到辅助线性表list
第二趟4个一对,每对内进行合并,也就是2 2 合并,这时上一次合并完成的元素存储在线性表list中,所以我们以原线性表L.elem作为辅助线性表,把元素从list合并到L.elem中,
第三趟…
就是这样L.elem和list轮流作为辅助线性表,直到一对中的元素大于总元素数量的两倍(d>2L.length),排序完成.
(为啥是d>2L.length?举个例子,假如元素数量9个,那么当一对8个时,线性表中就会有 4 4 合并,和1 0 合并,并没有完成9个元素的合并,需要进行16个一对的合并,就会有8 1 合并,所以d>2*L.length就是为了避免这种元素数量非2的整指数次方的情况)
3.每趟进行分对后,一对内有两组线性表,我们要对它们进行遍历排序合并,所以要分割成两组,大部分是对半分,但是并不是每对的元素数量都等于增量,比如总元素数量是9个,那么当以4个元素一对时,就会是2 2合并 2 2 合并 1 0合并,不全是2个一组,所以我们把一对线性表分割成两组线性表进行遍历时,并不都是对半分,要考虑在这种元素数量非2的整指数次方的情况
(我把每组线性表的起点和上限都进行了计算和判断,然后在起点和上限范围中进行遍历,就可以保证遍历位置不会出错,具体看代码)
4.排序完成后有序序列存储在哪个线性表?这个简单,我们用变量count记录合并趟数,如果是奇数就在list中,如果是偶数就在L.elem中,然后保存和释放对应的线性表就行了
三.代码实现
注意:顺序表中零单元不用,从索引1开始存储元素
//归并排序(排列为递增序列)
void MergeSort(SqList &L)
{
ElemType *list=(ElemType*)malloc(sizeof(ElemType)*(L.length+1)); //新建辅助顺序表
ElemType *p=NULL,*q=NULL;
int count,d,pos1,pos2,pos,l,r,t1,t2; //count排序趟数,d组距,pos1第一组元素遍历位置,pos2第二组元素遍历位置,pos合并组存储位置
for(count=1,d=2;d<=2*L.length;d*=2,count++)//l第一组左端,r第二组右端,t1第一组上限,t2第二组上限
{ //L.elem和list轮流作为辅助线性表
p=(count%2==1)?list:L.elem; //p为归并存储线性表
q=(count%2==1)?L.elem:list; //q线性表为要进行归并的线性表
for(l=1,r=l+d-1;l<=L.length;l+=d,r=l+d-1)
{
pos1=l; //第一组线性表起点
t1=(l+d/2-1)<L.length?(l+d/2-1):L.length; //第一组线性表上限
pos2=1+t1; //第二组线性表起点
t2=r<L.length?r:L.length; //第二组线性表上限
pos=l; //合并组线性表起点
while(pos1<=t1&&pos2<=t2) //将q中更小的元素先并入p中
{
if(q[pos1]<=q[pos2])
p[pos++]=q[pos1++];
else
p[pos++]=q[pos2++];
}
while(pos1<=t1)
p[pos++]=q[pos1++]; //将剩余的q[pos1..t1]复制到p
while(pos2<=t2)
p[pos++]=q[pos2++]; //将剩余的q[pos2..t2]复制到p
}
}
L.elem=p; //归并完成的线性表(p可能是原来的L.elem线性表,也可能是list线性表)
free(q); //释放辅助线性表内存(q可能是原来的L.elem线性表,也可能是list线性表)
}
操作结果