Leetcode 面试题51. 数组中的逆序对【回溯法&BIT】

4 篇文章 0 订阅
2 篇文章 0 订阅

问题描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

解题报告

归并排序方法

不解释。
时间复杂度: O ( n l o g n ) O(nlog n) O(nlogn)
空间复杂度: O ( n ) O(n) O(n)

BIT【参考官方答案】

【树状数组】是一种可以动态维护序列前缀和的数据结构,它的功能是:

  • 单点更新 update(i,v):将序列 i 位置的数加上一个值 v,这题 v =1
  • 区间查询 query(i):查询序列 [1···i] 区间的区间和,即 i 位置的前缀和
    修改和查询的时间代价都是 O ( l o g n ) O(log n) O(logn),其中 n 为需要维护前缀和的序列的长度。

创建一个数组 tree,其中 tree[i] 存储的是数值 i 在数组 nums中出现的次数。
nums的末尾开始遍历,计算 tree[nums[i]]-1 位置的前缀和,然后将 tree[nums[i]]1

时间复杂度:离散化时进行排序,时间代价为 O ( n l o g n ) O(nlog n) O(nlogn),单次二分的时间代价为 O ( l o g n ) O(log n) O(logn),一共有 n n n次,总时间代价为 O ( n l o g n ) O(nlog n) O(nlogn);循环执行 n n n 次,每次进行 O ( l o g n ) O(log n) O(logn) 的修改和 O ( l o g n ) O(log n) O(logn) 的查找,总时间代价为 O ( n l o g n ) O(nlog n) O(nlogn)。故渐进时间复杂度为 O ( n l o g n ) O(nlog n) O(nlogn)
空间复杂度:树状数组需要使用长度为 n 的数组作为辅助空间,故渐进时间复杂度为 O ( n ) O(n) O(n)

实现代码

归并排序实现

class Solution {
private:
    int cnt=0;
public:
    void mergesort(int lo,int hi,vector<int>& nums,vector<int>& tmp){
        if(lo>=hi) return;
        int mid=lo+(hi-lo)/2;
        mergesort(lo,mid,nums,tmp);
        mergesort(mid+1,hi,nums,tmp);
        int i=lo,j=mid+1;
        for(int k=lo;k<=hi;k++){
            if(i>mid) tmp[k]=nums[j++];//nums[i]到nums[mid]已经全部填入tmp
            else if(j>hi) tmp[k]=nums[i++];//nums[mid+1]到nums[j]已经全部填入tmp
            else if(nums[i]>nums[j]) {
                tmp[k]=nums[j++];
                cnt+=mid-i+1;//i肯定小于j,且nums[i]到nums[mid]是升序排序,如果nums[i]>nums[j],说明从nums[i]到nums[mid]和nums[j]都是逆序对
            }
            else tmp[k]=nums[i++];
        }
        for(int m=lo;m<=hi;m++) nums[m]=tmp[m];//
    }
    int reversePairs(vector<int>& nums) {
        vector<int> tmp(nums.size(),0);//就是用来记录某个递归函数merge后的情况,然后复制更新nums
        mergesort(0,nums.size()-1,nums,tmp);
        return cnt;
    }
};

BIT实现

class BIT {
private:
    vector<int> tree;
    int n;

public:
    BIT(int _n): n(_n), tree(_n + 1) {}

    static int lowbit(int x) {
        return x & (-x);
    }

    int query(int x) {
        int ret = 0;
        while (x) {
            ret += tree[x];
            x -= lowbit(x);
        }
        return ret;
    }

    void update(int x) {
        while (x <= n) {
            ++tree[x];
            x += lowbit(x);
        }
    }
};

class Solution {
public:
    int reversePairs(vector<int>& nums) {
        int n = nums.size();
        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);
        int ans = 0;
        for (int i = n - 1; i >= 0; --i) {
            ans += bit.query(nums[i] - 1);
            bit.update(nums[i]);
        }
        return ans;
    }
};

参考资料

[1] Leetcode 46. 全排列
[2] 题解区:guanzhanyi
[3] 题解区:官方答案

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值