归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
假设我们有一个没有排好序的序列,那么首先我们使用分割的办法将这个序列分割成一个一个已经排好序的子序列,然后再利用归并的方法将一个个的子序列合并成排序好的序列。分割和归并的过程可以看下面的图例。
1、算法基本思路
设两个有序的子文件(相当于输入堆)放在同一向量中相邻的位置上:a[start..mid],a[mid+1..end],先将它们合并到一个局部的暂存向量r(相当于输出堆)中,待合并完成后将r复制回a[start..end]中。
(1)合并过程
合并过程中,设置i,j和p三个指针,其初值分别指向这三个记录区的起始位置。合并时依次比较a[i]和a[j]的关键字,取关键字较小的记录复制到r[p]中,然后将被复制记录的指针i或j加1,以及指向复制位置的指针p加1。
重复这一过程直至两个输入的子文件有一个已全部复制完毕(不妨称其为空),此时将另一非空的子文件中剩余记录依次复制到r中即可。
(2)动态申请R1
实现时,R1是动态申请的,因为申请的空间可能很大,故须加入申请空间是否成功的处理。
2、自顶向下的方法(归并排序)
采用分治法进行自顶向下的算法设计,形式更为简洁。
(1)分治法的三个步骤
设归并排序的当前区间是a[start..end],分治法的三个步骤是:
①分解:将当前区间一分为二,即求分裂点
②求解:递归地对两个子区间a[start..mid]和R[mid+1..end]进行归并排序;
③组合:将已排序的两个子区间R[start..mid]和R[mid+1..end]归并为一个有序的区间R[start..end]。
递归的终结条件:子区间长度为1(一个记录自然有序)。
整体程序如下:
#include<iostream>
#include<stdlib.h>
#define error printf
using namespace std;
void Merge(int *a,int start,int mid,int end);
void Merge_sort(int *a,int start,int end);
int main()
{
int i,a[1000],start,end;
cout<<"Please input start and end:";
cin>>start>>end;
cout<<"Please input "<<end+1<<" numbers: ";
for(i=0;i<end+1;i++)
cin>>a[i];
Merge_sort(a,start,end);
cout<<"The result of resorted: ";
for(i=0;i<end+1;i++)
cout<<a[i]<<' ';
}
void Merge(int a[],int start,int mid,int end)
{//将两个有序的子文件a[start..mid)和R[mid+1..end]归并成一个有序的
int i=start,j=mid+1,p=0;
int *r;
r=(int *)malloc((end-start+1)*sizeof(int));
if(!r)//申请空间失败
error("insufficient memory available");
while(i<=mid&&j<=end)//两子文件非空时取其小者输出到r[p]上
r[p++]=(a[i]<a[j])?a[i++]:a[j++];
while(i<=mid) r[p++]=a[i++];//若第1个子文件非空,则复制剩余记录到r中
while(j<=end) r[p++]=a[j++];//若第2个子文件非空,则复制剩余记录到r中
for(i=start,p=0;i<=end;p++,i++)
a[i]=r[p];//归并完成后将结果复制回a[start..end]
}
void Merge_sort(int *a,int start,int end)
{
int mid;
if(start<end)//将两个有序的子文件a[start..mid)和R[mid+1..end]归并成一个有序的
{
mid=(end+start)/2;//分解
Merge_sort(a,start,mid);//递归地对a[start..mid]排序
Merge_sort(a,mid+1,end);//递归地对a[mid+1..end]排序
Merge(a,start,mid,end);//组合,将两个有序区归并为一个有序区
}
}