二路归并排序的算法
归并:
void merge(elemtype a[],int low,int mid, int high){
//mid将a分成左右low-mid,mid+1-high两部分
int i,j,k;
elemtype b[high-low+1];//辅助数组b的长度为high-low+1
for(i=low;i=high;i+) b[i]=a[i];//复制a到b
i=low;j=mid+1;k=low;
while(i<=mid&&j<=high){
if(b[i]<=b[j])
a[k++]=b[i++];
else a[k++]=b[j++];
}
while(i<=mid) a[k++]=b[i++];
while(j<=high) a[k++]=b[j++];
}
排序
void mergesort(elemtype a[],int low,int high){
if(low<high){
int mid=(low+high)/2;
mergesort(a,low,mid);
mergesort(a,mid+1,high);
merge(a,low,mid,high);
}
}
//注意:该算法是递归算法,第一趟归并不一定是两两归并,有可能会出现头两个归并后再和第三个归并。
改进:先对原始序列扫描一次,并把序列划分成各个有序子序列,
如1,3,4,2,5,8,7划分成(1,3,4),(2,5,8),(7)。再对各个子序列归并排序。
如何划分?划分就是要记下每个子序列的首尾,为了能够按顺序记录,我们用队列存储各子序列的头元素。
本算法用不带头节点的单链表结构。
void merge(linklist la,linklist lb,linklist &lc){
lnode *pa,*pb,*pc;
if(la&&lb){//选lc的第一个元素
if(la->data<=lb->data){
lc=la;pa=la->next;pb=lb;
}
else{
lc=lb;pa=la;pb=lb->next;
}
}//因为是不带头节点的,所以第一个元素应该特殊处理
pc=lc;
while(pa&&pb){//选la与lb的较小者,并用lc把这些较小节点的指针域连接起来
if(pa->data<=pb->data){
pc->next=pa;pc=pa;pa=pa->next;
}
else{
pc->next=pb;pc=pb;pb=pb->next;
}
}
//直接把剩余部分的指针连接到lc的尾部
if(pa) pc->next=pa;
if(pb) pc->next=pb;
}
void mergesort(linklist &r){
//先划分
initqueue(Q);
lnode *s,*t;
s=r;
enqueue(Q,s);//第一个元素入队
while(s->data<=s->next->data) s=s->next;
if(s){
t=s->next;//防止断链
enqueue(Q,t);//将有序子序列的头元素入队
s->next=null;
}
//排序
while(!empty(Q)){
dequeue(Q,t);
if(empty(Q)) break;//单个子序列无法归并,退出
dequeue(Q,s);
merge(t,s,r);
enqueue(Q,r);//归并后把新的子序列的首节点入队
}
}