这个是从小到大哒,类比一下可以写出从大到小哒,这里就先说从小到大
归并排序用到分治的思想,将大问题分成小问题再合并起来得到答案。
那么用在排序上,就是把一长串数字分成一小串一小串,分别排序,再合起来
合起来的时候,就要用到三个指针,保证每次将两个小串中最小的数字放入他俩合并成的串中
比如第一个串是b[1]=2 b[2]=4 b[3]=6,第二个串是a[1]=1 a[2]=3 a[3]=5;
第一个指针k1管b数组,第二个指针k2管a数组,第三个指针k管ans数组
一开始k1和k2都指在1,比较a[k2]和b[k1],发现a[k2]=1比较小,ans[k]=a[k2],k++,k2++;
这时ans[1]=1;
同理ans[2]=b[k2]=2,k2++,k++;
最终就可以得到六个数字合并起来的1 2 3 4 5 6;
(绿线是分界线,绿线上是代码中sort_re的部分,绿线下的排序思想主要是代码中sort_change的部分)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int temp[100005],a[100005];
void sort_change(int l,int mid,int r){
//排序部分,把大区间再次分成小区间排序
int k1=l,k2=mid+1,k=l;
//初始化三个指针,一个指左区间左端点,一个指右区间左端点,一个指最终答案区间的左端点
while(k1<=mid&&k2<=r){
if(a[k1]>a[k2]){
temp[k]=a[k2];
k++,k2++;
}
else{
temp[k]=a[k1];
k++,k1++;
}
//每次取两个区间中最小的数字加入答案,指针右移
}
while(k1<=mid){
temp[k]=a[k1];
k++,k1++;
}//如果右区间的数字已经取完了,将左区间剩余数字按照一样从小到大的顺序放入答案
while(k2<=r){
temp[k]=a[k2];
k++,k2++;
}//如果左区间的数字已经取完了,将右区间剩余数字按照一样从小到大的顺序放入答案
for(int i=l;i<=r;i++) a[i]=temp[i];//将储存答案的数组的值赋回原来的数组
}
void sort_re(int l,int r){
if(l>=r) return ;//如果该区间不满足条件,即左边在右边的右边,return
int mid=(l+r)/2;//二分思想
sort_re(l,mid);//给左子区间排序
sort_re(mid+1,r);//给右子区间排序
sort_change(l,mid,r);//保证左右子区间排列得整整齐齐之后,才能并起来
//将大区间化成小区间然后排序
}
int n;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);//读入
sort_re(1,n);//排序(进入划分区间)
for(int i=1;i<=n;i++) printf("%d ",a[i]);//输出
return 0;
}
我的做法是提前开好temp数组,存储临时答案QAQ
很多人好像都是在函数中开一个数组 也许这样比较省空间吧QAQ
代码应该算比较简洁了QAQ
注意二分的mid,l,r三个数字,最好自己模拟验证一下
比如,我这里分区间(sort_re)的时候,是l~mid,mid+1~r;
模拟一下,l=1,r=2,(int)mid=1,所以我分的区间就是1~1和2~2(全部return掉啦)
接下来,sort_change(1,1,2);
l=1,mid=1,r=2;
k1=1,k2=1+1=2,k=1;
即左子区间的数字是a[1],右子区间的数字是a[2] ; (因为举的例子区间很小所以每个区间只有一个数字啦)
那么这时候,k1的取值范围应该是{1},所以while的条件应该为k1<=mid而不是k1<mid;