求逆序对 (用归并) nlogn

逆序对 在线测评 洛谷:
https://www.luogu.org/problem/show?pid=1908
什么是逆序对??
对于一个包含N个非负整数的数组A[1..n],如果有i < j,且A[ i ]>A[ j ],则称(A[ i] ,A[ j] )为数组A中的一个逆序对。(来自百度百科)

怎么求 有一个广为人知的 n方 的算法 就是枚举
在此讲一下 用归并排序 复杂度是nlogn的算法

贴代码:

#include<iostream>
#include<cstdio>
using namespace std;
const int INF=40010;
int a[INF];
int t[INF];
int cnt;
void mid_sort(int x,int y)
{

    if(y-x>1)//我们排序的是[x,y)的 所以当y-x为一时就只剩下一个数了 此时 我们就没有必要給一个数排序了 
    {
        int m;//  m时x和y中点 
        m=x+(y-x)/2; 
        int l=x;
        int r=m;
        int i=x;
        mid_sort(x,m);/*这是递归来排序*/ 
        mid_sort(m,y);//同上 
        while(l<m||r<y) //从这里往下是归并排序中的 合并两串已经排好序的数 
        {
            if(r>=y||(l<m && a[l]<=a[r])) 
            t[i++]=a[l++];
            else{
            t[i++]=a[r++];
            cnt+=m-l;                       //在下面会贴出来 
            }
        }
        for(int i=x;i<y;i++)
        {
            a[i]=t[i];
        }                                    //从这里往上  
    }
}
int main ()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    mid_sort(1,n+1);/*因为归并排序 是排序[1,n)的数,所以我们要把n+1*/ 
    cout<<cnt;
    return 0;
}
        while(l<m||r<y)                      
        {
            if(r>=y||(l<m && a[l]<=a[r])) 
            t[i++]=a[l++];
            else{
            t[i++]=a[r++];
            cnt+=m-l;        
            }
        }
        for(int i=x;i<y;i++)
        {
            a[i]=t[i];
        }   

首先讲一下归并排序 会的直接跳过就好
先看其中的判断

if(r>=y||(l<m && a[l]<=a[r]))

如果我们只是单纯的去比较当前的左区间和右区间 当前的数的大小从而决定将哪个数放入临时数组 这样是有一个bug 的 就是如果有一个区间空了怎么办
所以 我们在此分为三种情况
其中第一种情况是:如果右区间为空 此时还在while循环里 也就是说 左区间一定非空 所以此时我们只需要将左区间剩下的数复制进临时数组即可。
另一种情况是:如果右区间不是空的 此时如果左区间也是非空的(这就是l < m的意义) 且 a[l]<=a[r] 我们就复制左区间的数。
剩下的情况就是复制右区间的条件了。

     for(int i=x;i<y;i++)
    {
        a[i]=t[i];
    }

这是将临时数组中的数复制到原数组的过程
end
接下来是逆序对的求法
很简单只需要加一条语句就行
在刚刚的代码已经贴出来了:

        cnt+=m-l;  

cnt 是逆序对的总数
为什么这样做:
当我们从右区间拿出来一个数a之后 此时就代表着左区间剩下的数一定比当前从右区间拿出的数大 所以此时左区间剩下的数就是有关a的逆序对数
end

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值