归并排序——codevs1076

纪念下AC的归并排序类型题……大佬勿喷,欢迎指出错误。

归并排序是最近学会的一类排序方法(感谢“没有梦想__何必远方“),O(nlog2n)的复杂度,虽然和algorithm中的sort复杂度一样,但在做(装)题(逼)中也是经常用到的,比如我将会在本周内发布的另一题解——codevs1688求逆序对中就会有很大用处。

这道题目虽然是一道水题,但用来练习归并排序也是一道非常好的题目,因此,下面的讲解并非最短题解,而是对归并排序的讲解,请注意!~~~

首先,归并排序的主要思想是——

将两个或两个以上的有序表组合成一个新有序表。

这也就决定了,我们将使用递归的方法来完成这一排序。

那么,既然已经知道了大体思路,就应该讲讲怎么实现。
(先盗用一张图)

这里写图片描述

大概从图片中可以看出,归并排序就是一次次的二分,然后再找到叶子节点后就开始两两比较,将小的存入临时数组,当遍历(O(n))完成后,就将临时数组复制到原数组中,这样,原数组就可以完成第一次的归并,然后在回溯中(O(logn))完成剩余排序,是一种十分巧妙的排序方法。

首先是递归的程序:

void MergeSort(int st,int en)
{
    int mid=(st+en)/2;
    if(st//遍历左子树
        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]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 
#include 
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]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(st1,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:由于作者是一名资深蒟蒻,有错误也是不可避免的事,所以欢迎大家指出错误,我也会改正的,谢谢。

你可能感兴趣的:(归并排序——codevs1076)