归并排序
归并排序是利用分治算法的一个典型例子,并且利用了二路归并算法,二路归并就是将两个有序数组合并成一个有序数组的过程,先介绍二路归并算法。
以升序数组为例,假设已经有两个排好序的数组。
a 1 3 4 4 7 11
b 2 5 6 8 8
二路归并的思想就是先新建一个数组c,然后比较两个有序数组开头的元素大小,将小的先放入c数组,然后再比较再放入,直到全部放完为止,简单的思考一下就能知道这样选出来的一定也是有序的。
简单的模拟一下过程就是:
1<2所以1先放入数组c
a 3 4 4 7 11
b 2 5 6 8 8
c 1
接着2<3所以2先放入数组c
a 3 4 4 7 11
b 5 6 8 8
c 1 2
接着3<5所以3先放入数组c
a 4 4 7 11
b 5 6 8 8
c 1 2 3
之后就是循环这个过程直到某一个数组已经放完了,接着再将另外一个数组的元素全部放入即可。
代码如下:
void merge(int a[],int len_a,int b[],int len_b,int c[]) //分别是a数组,a数组的长度,b数组,b数组的长度,c数组
{
//升序排序
int i,j,k;
i=0; //i代表a数组的开头
j=0; //j代表b数组的开头
k=0; //k代表c数组的要放的位置
while(i<len_a&&j<len_b){
if(a[i]<b[j]){
c[k++]=a[i++];
//如果a开头比b开头小,将a开头放入c数组并且更新i的位置
}else{
c[k++]=b[j++];
//反之
}
}
while(i<len_a) c[k++]=a[i++];
while(j<len_b) c[k++]=b[j++];
//当没有放完的那个数组中的元素全部放入c数组
//值得注意的是c数组的长度必须超过len_a+len_b否则就会溢出
}
接下来要介绍的就是归并排序了,运用了分治的思想,就是将数组分成两段,等这两段有序后再二路归并直到排好序,但是分成两段之后其中某一段怎么排序呢,肯定不是用别的排序方法而是再次使用归并排序将其分成两段直到这一段长度只有1不能再分了。
可以很容易想到再次使用归并排序本身这个过程就是一个递归的过程。
但是不同于上面的a数组和b数组是两个分开的数组,递归排序中a数组和b数组其实都是待排序数组中的一部分,所以我们通常使用两个变量来确定其中某一个数组——分别是记录开头和记录末尾位置,再加上待排序的数组本身,所以这个归并排序一共有三个形参分别是。
void merge_sort(int num[],int start,int end)
然后再来确定结束条件为这一段的长度小于1时,那么这一段的长度我们确定为end-start+1,所以就有结束条件
if(end-start+1<=1){ //等价于end<=start
return ;
}else{
}
那么接下来就很好做了,就是当满足条件时将数组分成两部分,分别进行归并,最后再二路归并排序
void merge_sort(int num[],int start,int end)
{
if(start<end){ //结束条件的else部分
int mid=(start+end)/2; //借用一个mid来划分数组
merge_sort(num,start,mid); //递归操作
merge_sort(num,mid+1,end);
merge(num,start,mid,end); //二路归并
}
}
二路归并和上面的类似,如果理解了就很好写出来,这里就不贴代码了,稍有不同的是因为是在一个数组上的两部分归并所以要新开一个c数组,然后再将最后结果复制给num数组,否则就覆盖掉自己了。
验证递归时要注意不要一层层递归都算出来,只需要看看开头和最后是否正确,否则就太过麻烦了。