我又来写博客了,上面两篇写了两篇难度比较大的,实际上在NOIP中很少用到,所以本人决定写一个比较好懂而且NOIP中实用的算法。这次的话,就写个归并排序吧。
我是一名C++ 选手,所以我知道很多C++ 选手都会使用STL标准模版库,包括sort(排序),但是有的时候有的题目的话,就会出现一些很明显的问题,就是STL所解决不了的(例如有特殊要求的题目,时间卡得很死的题目),所以本人正在学习STL模版的具体实现,我也强烈建议C++选手能懂得STL的具体实现(当然在NOIP中能使用STL最好是尽量使用,多争取时间来写别的题目,但是注意STL模版的时间复杂度比较高,容易超时。在平时刷题的时候,写一遍STL熟悉一下,然后最好再自己不用STL实践一下)
今天我来讲一下归并排序。为什么我要讲这个排序,而先不讲快速排序和桶排序呢?实质上是有一个比较著名的题型与其相关——就是求一个序列中的逆序对(在数列中有1<=i,j<=n,i<j 并且 a[i]>a[j])
首先是基本的归并排序的讲解
归并排序采用的是分治的思想,按照分治三步法,归并排序的步骤如下:
1.划分问题:把序列对半分开,使得两半的数据量基本相等;
2.排序问题:把两边的序列分别排序,使得两边都有序(当序列长度为1时,序列有序,直接进行比较);
3.合并问题:把两个序列按照大小顺序合并(其实合并的时候就是把两个数组的开头进行比较,把符合要求的那一个放到一个临时空间里,并从其相应的序列中删除就是了,然后一个个比较,合并的前提是两个序列都分别有序)。
归并排序的过程实际上是一种递归的问题,其时间复杂度为O(nlogn),一遍遍地进行二分操作,直到序列长度为1,然后再比较合并(这就是所谓的归并排序),然后回溯,最后将临时空间里面的变量全部复制到数组中,在序列长度合并为原来的长度的时候,讲临时空间里面的变量全部复制到数组中,这样归并排序就完成了。
这里不再多说,下面给出核心代码:
![](https://i-blog.csdnimg.cn/blog_migrate/a2b7024cb6a8cccd23244148c90016f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/aa7790439652fcf252898472b6dc57b7.gif)
1 void merge_sort(int* A,int l,int r,int* T){//A为数组序列,T为临时空间 2 if(r - l >1){//因为如果区间内只有一个数的话,就不必排序 3 int m = l + (r - l)/2; 4 int p = l,q = m,i=l; 5 merge_sort(A,l,m,T); 6 merge_sort(A,m,r,T); 7 while(p < m || q < r){ 8 if(q >= r || (p < m && A[p] <= A[q]))T[i++] = A[p++];//从左半部分复制到临时空间 9 else T[i++] = A[q++];//从右半部分复制到临时空间 10 } 11 for(int i = l;i <= r;i++)A[i] = T[i]; //从临时空间复制回A数组 12 } 13 }
这里呢就是归并排序的核心代码,下面我们来讲一下逆序对问题
关于这个逆序对的问题,其实也很简单,上面我们也给出了逆序对的定义,其实在归并排序的时候,我们完全可以金鑫逆序对的计算,其实道理很简单。当我们归并排序的时候加一步,就是当我们从右半部分的元素放入临时空间T的时候,会产生m-p个逆序对,所以其实在我们进行比较排序,将右半部分的元素放入临时空间T的时候,我们只需要同时统计逆序对的个数就可以了,其实就是加了一条语句:当我们以cnt变量来计数的时候,那么在else语句中再加一条cnt += m-p;即可。那么我还是给出核心代码,核心代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/a2b7024cb6a8cccd23244148c90016f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/aa7790439652fcf252898472b6dc57b7.gif)
1 void merge_sort(int* A,int l,int r,int* T){//A为数组序列,T为临时空间 2 if(r - l >1){//因为如果区间内只有一个数的话,就不必排序 3 int m = l + (r - l)/2; 4 int p = l,q = m,i=l; 5 merge_sort(A,l,m,T); 6 merge_sort(A,m,r,T); 7 while(p < m || q < r){ 8 if(q >= r || (p < m && A[p] <= A[q]))T[i++] = A[p++];//从左半部分复制到临时空间 9 else{ 10 T[i++] = A[q++];//从右半部分复制到临时空间 11 cnt += m-p;//统计逆序对数 12 } 13 } 14 for(int i = l;i <= r;i++)A[i] = T[i]; //从临时空间复制回A数组 15 } 16 }
好了,这次归并排序的讲解博客就到这里了,其实这个归并排序,是所有排序算法中最为重要的一个,也是效率最高的一个,希望大家在看不懂的地方给我提下意见,好能让我修改一下,谢谢大家啦,这样我会有更充足的动力写下去。