[模版] 归并排序

来源:互联网 发布:签证 生物识别数据 编辑:程序博客网 时间:2024/06/13 20:35

    • 引子
    • 原理
    • 模板代码
    • 例题逆序对
    • 分析
    • 题解代码
  • GG

引子

归并排序是分治算法中一个很重要的应用。它的思想就是把一个序列尽量分成相等长度的两个子序列,一直分到子序列长度为1。然后再两两按顺序合并,最后得到的序列也是有序的。这种排序手段时间复杂度是O(nlogn),而且是一种稳定的排序方法。相等元素的顺序不会发生改变。。。

原理

原理也不瞎扯了,假设我们要合并这两个已经有序的子序列:
2 4 6 8 1 4 6 7
并且要放到辅助数组r中
那么i=1,mid=4,j=5,t=8,k=1;
先比较2和1,发现1<2,那么把1
放到r[1]中,
现在r数组是:
1 0 0 0 0 0 0 0 ,并且k=2;
然后比较2和4,2<4,就把2放进去,r数组:1 2 0 0 0 0 0 0,然后比较4和4,因为4<=4成立(这边其实放哪个4
都没关系。),就把左子序列的4放进r数组,现在:1 2 4 0 0 0 0 0,然后比较6 和4,把4放进去,现在:1 2 4 4 0 0 0 0,然后比较6 和 6,把左序列的6放进去,现在:1 2 4 4 6 0 0 0,然后6和7比较,把6放进去,现在:1 2 4 4 6 6 0 0,然后7和8比较,把7放进去,现在:1 2 4 4 6 6 7 0,然后我们发现右序列已经空了,而且左序列还有剩余,然后把左序列全部剩余元素都复制到r中,现在:1 2 4 4 6 6 7 8,就排好序了。
你们,是不是就,听不懂了。

还是上代码吧。

模板代码

#include<bits/stdc++.h>using namespace std;int ans=0,a[50000],r[50000];//a是待排序的数组,r数组是归并排序的辅助数组。void msort(int s,int t)//归并排序{    if(s==t) return;    int mid=(s+t)/2;    msort(s,mid);    msort(mid+1,t);//不断分解成两个尽量等长的子序列。    int i=s,j=mid+1,k=s;//开始合并,i(s)~mid是左子序列,j(mid+1)~t是右子序列。我k标记的是当前元素需要放入辅助数组的位置。我们需要明确的是,当我们开始合并,左右子序列都已经是有序的了。    while(i<=mid&&j<=t)    //当两个子序列都非空的时候,要进行比较。    {        if(a[i]<=a[j])        r[k++]=a[i++];//如果前者小于后者,那么把前者放入r[k]中。        else        r[k++]=a[j++];//反之,把后者放入r[k]中    }    //结束循环的时候肯定是有一个或者两个子序列已经全部放入辅助空间了。那么剩下那个子序列肯定比所有已经放入辅助空间的元素都大,所以只要按顺序全部放到辅助空间就可以了。    while(i<=mid)//如果左序列不为空    r[k++]=a[i++];    while(j<=t)//如果右序列不为空    r[k++]=a[j++];    for(int i=s;i<=t;i++)    a[i]=r[i];//把辅助数组中的元素复制回原数组,本序列已经有序,可以递归返回上一层合并更大的两个序列了。}int main(){    int n;    scanf("%d",&n);    for(int i=1;i<=n;i++)    scanf("%d",&a[i]);//日常读入    msort(1,n);    printf("%d",ans);    return 0;}

例题——逆序对

题目描述
猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai> aj且i< j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。
输入输出格式
输入格式:
第一行,一个数n,表示序列中有n个数。
第二行n个数,表示给定的序列。
输出格式:
给定序列中逆序对的数目。
输入输出样例
输入样例#1:
6
5 4 2 6 3 1
输出样例#1:
11
说明
对于50%的数据,n≤2500
对于100%的数据,n≤40000。

分析

这道题目完全可用冒泡排序来做,但是看看数据,n^2的时间是肯定会超时滴。我们思考下,归并排序有一个很重要的性质:稳定,因为相同的元素是不会交换的,只有逆序的元素才会交换,换句话说,只要右序列的元素在左序列元素之前进r数组,那就产生了若干对逆序对,产生逆序对的数量就是左序列剩余元素个数。

题解代码

这边的代码跟模板几乎一模一样,就是多了一句统计逆序对数量的语句。

#include<bits/stdc++.h>using namespace std;int ans=0,a[50000],r[50000];void msort(int s,int t){    if(s==t) return;    int mid=(s+t)/2;    msort(s,mid);    msort(mid+1,t);    int i=s,j=mid+1,k=s;    while(i<=mid&&j<=t)    {        if(a[i]<=a[j])        r[k++]=a[i++];        else        {            r[k++]=a[j++];                ans+=mid-i+1;//跟前面归并排序唯一一点不同的地方,统计逆序对数量,左序列剩余的元素就是mid-i+1.所以累加就可以了        }    }    while(i<=mid)    r[k++]=a[i++];    while(j<=t)    r[k++]=a[j++];    for(int i=s;i<=t;i++)    a[i]=r[i];}int main(){    int n;    scanf("%d",&n);    for(int i=1;i<=n;i++)    scanf("%d",&a[i]);    msort(1,n);    printf("%d",ans);    return 0;}

GG

原创粉丝点击