合并函数
2-路归并排序的核心在于如何将两个有序序列合并为一个有序序列。
void merge(vector<int>& a,int L1,int R1,int L2,int R2) {
int i=L1,j=L2;
//定义一个足够大的临时容器
int size=(R1-L1+5)*2;
vector<int> tmp(size);
int index=0;
//把两组中较小的插入
while(i<=R1&&j<=R2) {
if(a[i]<a[j]) tmp[index++]=a[i++];
else tmp[index++]=a[j++];
}
//把末尾的插入
while(i<=R1) tmp[index++]=a[i++];
while(j<=R2) tmp[index++]=a[j++];
//把临时容器中的值赋值回原容器
for(i=0; i<index; i++) {
a[L1+i]=tmp[i];
}
}
递归实现
递归实现写法简单,只需要注意左边界一定要小于右边界即可。
void mergeSort(vector<int>& a,int left,int right) {
if(left<right) {
int mid = (left+right)/2;
mergeSort(a,left,mid);
mergeSort(a,mid+1,right);
merge(a,left,mid,mid+1,right);
}
}
非递归实现
如果希望输出归并排序每一趟的结果,就只能使用非递归的写法。
主要思想:令step为2的幂次,每次把容器中step个元素作为一组,进行内部合并;每次令step乘以2,重复操作,直到step/2超过元素总数,说明已经完成了汇总。
void mergeSort_non(vector<int>& a) {
//step超过容器大小后,还需要再执行一趟总的归并
for(int step=2; step/2<a.size(); step*=2) {
for(int i=0; i<a.size(); i+=step) {
//左区间元素个数为step/2
//拿第一组记忆mid的写法:一组两个元素,左右区间各一个元素,所以L1为0,R1为0,L2为1,R2为1
//所以mid最开始应该为0,需要-1
int mid=i+step/2-1;
int n=a.size()-1;
merge(a,i,mid,mid+1,min(i+step-1,n)) ;
}
//输出每一趟的结果
for(auto i:a) {
cout<<i<<" ";
}
cout<<endl;
}
}
时间允许的时候可以使用sort函数代替merge函数,注意sort函数对迭代器的写法,末位是尾后一位元素,所以不用-1。
void mergeSort_non_recur(vector<int>& a){
//step超过容器大小后,还没有进行总的归并,因此需要比较的是step/2和size的大小
for(int step=2;step/2<a.size();step*=2){
for(int i=0;i<a.size();i+=step){
//注意右边界是容器大小和该组右边界较小的,不要越界
sort(a.begin()+i,min(a.begin()+i+step,a.end()));
}
//输出每一趟的结果
for(auto i:a){
cout<<i<<" ";
}
cout<<endl;
}
}