【剑指offer】数组中的逆序对

题目描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

输入描述:
 
  

题目保证输入的数组中没有的相同的数字

数据范围:

对于%50的数据,size<=10^4

对于%75的数据,size<=10^5

对于%100的数据,size<=2*10^5

示例1

输入
复制
 
  
输出
复制
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

黑色数组代表原来的数组(下面用A[i]代替),红色结构代表我们的树状数组(下面用C[i]代替),发现没有,每个位置只有一个方框,令每个位置存的就是子节点的值的和,则有

  • C[1] = A[1];
  • C[2] = A[1] + A[2];
  • C[3] = A[3];
  • C[4] = A[1] + A[2] + A[3] + A[4];
  • C[5] = A[5];
  • C[6] = A[5] + A[6];
  • C[7] = A[7];
  • C[8] = A[1] + A[2] + A[3] + A[4] + A[5] + A[6] + A[7] + A[8];
 
 
 
 
 
 
 
 
 
 
 
 
class Solution
{
public:
#define max_v 9999999

    int c[max_v];

    int lowbit(int x)//找到x二进制下等于1的最低位
    {
        return x&(-x);
    }
    void update(int x,int d)//第x位置上的元素加上d
    {
        while(x<max_v)
        {
            c[x]+=d;
            x+=lowbit(x);//更新后面的数组值
        }
    }
    int getsum(int x)//求得小于等于x的和,因为元素的值为1,代表该元素出现过,也就是求得小于等于x的元素个数
    {
        int res=0;
        while(x>0)
        {
            res+=c[x];
            x-=lowbit(x);//获得前面的数组值的下标
        }
        return res;
    }
    int InversePairs(vector<int> data)
    {
        int sum=0;
        for(int i=0; i<data.size(); i++)
        {
            int x=data[i];
            x++;
            //伴随着x输入序列,统计此时序列中大于x的元素的个数
            //大于x的元素的个数=此时总元素个数-小于等于x的元素个数
            sum=(sum%1000000007+(i-getsum(x))%1000000007)%1000000007;
            update(x,1);
        }
        sum%=1000000007;
        return sum;
    }
};


解法2:归并排序求解逆序数

归并排序中先是划分到底,直到需要并的两个子数组中只有一个数字,这两个字序列就是分别有序的,然后两个有序序列继续合并,我们所做的处理就是在两个有序序列合并的过程中进行计算即可

现在有两个子序列,下标分别是[s,m],[m+1,t]

当前面子序列中某个元素i大于后面子序列中某个元素j时,元素i到m之间的元素肯定都是大于元素j的,因为数组是有序的,那么他们和元素j都可以组成逆序对,所以我们需要知道此时i元素到m元素之间元素的数量,其数量可以表示为m-i+1,也可以表示为j-k,k代表放入新数组中已经有序的数字,这两种表示方法都是同一个意思

归并排序求解逆序数的时间复杂度为:O(N*log N)

归并求解逆序数

#include<bits/stdc++.h>
#include<stdio.h>
#include<memory>
#include<vector>

using namespace std;

class Solution
{
public:
#define max_v 2*100002

    int temp[max_v];
    int a[max_v];
    int  ans;

    void mer(int s,int m,int t)
    {
        int i=s;
        int j=m+1;
        int k=s;
        while(i<=m&&j<=t)
        {
            if(a[i]<=a[j])
            {
                temp[k++]=a[i++];
            }
            else
            {
                ans+=(m-i+1);
                //ans+=j-k;//求逆序数
                ans%=1000000007;
                temp[k++]=a[j++];
            }
        }
        while(i<=m)
        {
            temp[k++]=a[i++];
        }
        while(j<=t)
        {
            temp[k++]=a[j++];
        }
    }
    void cop(int s,int t)
    {
        for(int i=s; i<=t; i++)
            a[i]=temp[i];
    }
    void megsort(int s,int t)
    {
        if(s<t)
        {
            int m=(s+t)/2;
            megsort(s,m);
            megsort(m+1,t);
            mer(s,m,t);
            cop(s,t);
        }
    }
    int InversePairs(vector<int> v)
    {
        int n=v.size();
        for(int i=0; i<n; i++)
            a[i]=v[i];

        megsort(0,n-1);
        return ans%1000000007;
    }

};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值