纪念下AC的归并排序类型题……大佬勿喷,欢迎指出错误。
归并排序是最近学会的一类排序方法(感谢“没有梦想__何必远方“),O(nlog2n)的复杂度,虽然和algorithm中的sort复杂度一样,但在做(装)题(逼)中也是经常用到的,比如我将会在本周内发布的另一题解——codevs1688求逆序对中就会有很大用处。
这道题目虽然是一道水题,但用来练习归并排序也是一道非常好的题目,因此,下面的讲解并非最短题解,而是对归并排序的讲解,请注意!~~~
首先,归并排序的主要思想是——
将两个或两个以上的有序表组合成一个新有序表。
这也就决定了,我们将使用递归的方法来完成这一排序。
那么,既然已经知道了大体思路,就应该讲讲怎么实现。
(先盗用一张图)
大概从图片中可以看出,归并排序就是一次次的二分,然后再找到叶子节点后就开始两两比较,将小的存入临时数组,当遍历(O(n))完成后,就将临时数组复制到原数组中,这样,原数组就可以完成第一次的归并,然后在回溯中(O(logn))完成剩余排序,是一种十分巧妙的排序方法。
首先是递归的程序:
void MergeSort(int st,int en)
{
int mid=(st+en)/2;
if(st<en)
{
MergeSort(st,mid);//遍历左子树
MergeSort(mid+1,en);//遍历右子树
Merge(st,mid,en);//对当前这一段进行归并
}
}
没什么可讲的,对吧……
然后就是最关键的部分了,如何完成排序操作?
我的思路大概是:首先二分目前的数组变成两个部分,分别各用一个指针指向两段数组的最前端,然后比较大小,小的存入临时数组,大的就留下继续比较。
由于任何一个指针都需要小于该段的长度,但如果该段并未遍历完怎么办?那么,我们就直接将剩下的一堆直接插到临时数组的最后,留给下一次的遍历去比较。
上代码吧:
void Merge(int st,int mid,int en)
{
int i=st;//左子树首指针
int j=mid+1;//右子树首指针
int k=0;
int t[100010];//临时数组,存储排好序的数列
while(i<=mid&&j<=en)
{
if(m[i]<m[j])
{
t[++k]=m[i];
i++;
}
else
{
t[++k]=m[j];
j++;
}
}
//第一次归并,比较大小
while(i<=mid)
{
t[++k]=m[i];
i++;
}
while(j<=en)
{
t[++k]=m[j];
j++;
}
//第二次归并,直接将剩下的插到队末
for(int i=1;i<=k;i++)
m[st+i-1]=t[i];
//记得将原数组赋上排好序后的值
}
对了,整段代码中最容易出错的是:
for(int i=1;i<=k;i++)
m[st+i-1]=t[i];
为什么这样说呢,因为我错……哦不,因为这一段中临时数组是从1(0也可以,随便你)开始存的,而我们的原数组的赋值应该从该段数组的开头,也就是st开始,最后还要注意是st+i-1哦!
好的,最后就上一下这道水题的题解吧(注释请看上面):
#include <iostream>
#include <cstdio>
using namespace std;
int m[100010];
void Merge(int st,int mid,int en)
{
int i=st;
int j=mid+1;
int k=0;
int t[100010];
while(i<=mid&&j<=en)
{
if(m[i]<m[j])
{
t[++k]=m[i];
i++;
}
else
{
t[++k]=m[j];
j++;
}
}
while(i<=mid)
{
t[++k]=m[i];
i++;
}
while(j<=en)
{
t[++k]=m[j];
j++;
}
for(int i=1;i<=k;i++)
m[st+i-1]=t[i];
}
void MergeSort(int st,int en)
{
int mid=(st+en)/2;
if(st<en)
{
MergeSort(st,mid);
MergeSort(mid+1,en);
Merge(st,mid,en);
}
}
int main()
{
int n;
cin >> n;
for(int i=1;i<=n;i++)
cin >> m[i];
MergeSort(1,n);
for(int i=1;i<=n;i++)
cout << m[i] << " ";
cout << endl;
return 0;
}
额,如果说我把这道题做复杂了,那我也无言以对,只是在做1688逆序对之前做个铺垫吧。
最后,欢迎大家继续关注我接下来的题解,我将在这周内完成codevs1688求逆序对的题解。
ps:由于作者是一名资深蒟蒻,有错误也是不可避免的事,所以欢迎大家指出错误,我也会改正的,谢谢。