剑指 Offer 51. 数组中的逆序对

题目

力扣

思路一 归并排序

分治法,把数组中元素不断分成一半排序,再把排序后的结果合在一起。合在一起时,用双指针,如果右边的数比左边的小就增加相应逆序对的个数。

代码一

class Solution {
public:
    // vector<int> tmp;
    int reversePairs(vector<int>& nums) {
        int n=nums.size();
        // tmp.resize(n);
        vector<int> tmp(n);
        return n==0?0:mergeSort(nums,tmp,0,n-1);
    }

    int mergeSort(vector<int>& nums,vector<int>& tmp,int l,int r){
        if(l>=r) return 0;
        int mid=l+r>>1;
        int res=mergeSort(nums,tmp,l,mid)+mergeSort(nums,tmp,mid+1,r);
        // vector<int> tmp(nums.size());
        // tmp=nums;
        //下面这种写法只需要复制当前需要归并的,上面那种是全部复制,会超时
        for(int k=l;k<=r;k++)
            tmp[k]=nums[k];
        int i=l,j=mid+1,k=l;
        while(k<=r){
            if(i==mid+1)
                nums[k++]=tmp[j++];
            else if(j==r+1 || tmp[i]<=tmp[j])
                nums[k++]=tmp[i++];
            else{
                nums[k++]=tmp[j++];
                res+=mid-i+1;
            }
        }
        return res;
    }
};

思路二  树状数组

从后往前遍历数组,把当前元素放入树状数组中,并累加当前树状数组中小于当前数的数的个数。但由于原数组的范围可能非常大,直接把元素值放入树状数组中会导致数组非常稀疏。因此,需要实现一个优化,先把原数组复制一份并排序,再把数据的相对位置(即排序后的排名)记录下来,树状数组要以1开始,所以要加1。这里用到了lower_bound函数,目的是在升序排序的数组中找到第一个大于等于num的指针。upper_bound是在升序排序的数组中找到第一个大于num的指针。

代码二

class BIT{
    int n;
    vector<int> tree;
public:
    BIT(int _n):n(_n),tree(_n+1){};
    static int lowbit(int x){
        return x&(-x);
    }

    //求树状数组中小于等于x的所有数之和
    int getSum(int x){
        int sum=0;
        while(x){
            sum+=tree[x];
            x-=lowbit(x);
        }
        return sum;
    }

    //每添加一个数,树状数组中所有由这个数组成的元素都要++
    void update(int x){
        for(;x<=n;x+=lowbit(x))
            ++tree[x];
    }
};

class Solution {
public:
    int reversePairs(vector<int>& nums) {
        int n=nums.size(),ans=0;
        //离散化
        vector<int> tmp=nums;
        sort(tmp.begin(),tmp.end());
        for(int& num:nums){
            num=lower_bound(tmp.begin(),tmp.end(),num)-tmp.begin()+1;
        }
        //树状数组统计逆序对
        BIT bit(n);
        for(int i=n-1;i>=0;i--){
            ans+=bit.getSum(nums[i]-1); //要严格小于,不统计当前这个数,所以要-1
            bit.update(nums[i]);
        }
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值