LeetCode——第二十天(数组中的逆序对)(树状数组)

LeetCode——第二十天
面试题51. 数组中的逆序对

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

示例 1:

输入: [7,5,6,4]
输出: 5


限制:

0 <= 数组长度 <= 50000

分析:逆序对,经典题目。用了两个for循环,结果超时了,哈哈哈,也很正常。然后印象中二叉树就是类似堆的线段树还是啥,可以一边更新一边统计,不过就接触一次,忘了。通过归并排序,原理很简单,大家看看就知道了。

暴力破解代码

class Solution {
public:
    int reversePairs(vector<int>& nums) {
        int res = 0;
        for(int i=0;i<nums.size();i++){
            for(int j=i+1;j<nums.size();j++){
                if(nums[i]>nums[j])res++;
            }
        }
        return res;
    }
};

归并排序代码

class Solution {
private:
    int res = 0;
public:
    //归并排序算法
    void mergesort(vector<int>& nums,vector<int>& temp,int lo,int hi){
        if(lo>=hi) return;//出口
        int mid = lo+(hi-lo)/2;
        mergesort(nums,temp,lo,mid);
        mergesort(nums,temp,mid+1,hi);
        int i=lo,j=mid+1,k=lo;
        //子序列排序过程
        while(i!=mid+1 && j!=hi+1){
            if(nums[i]>nums[j]){
                temp[k++] = nums[j++];
                res+=mid-i+1;
                /*这里统计,如果左边比右边大,那么就是逆序,
                并且左边剩下都会比当前右边这个值大,所以全部加进去*/
            }
            else temp[k++] = nums[i++]; 
            //需要注意,如果右边等于或者大于左边,是正常情况,继续排序即可
        }
        while(i!=mid+1) temp[k++] = nums[i++];
        while(j!=hi+1) temp[k++] = nums[j++];
        for(int i=lo;i<=hi;i++) nums[i] = temp[i];
    }
    int reversePairs(vector<int>& nums) {
        vector<int> temp(nums.size(),0);
        mergesort(nums,temp,0,nums.size()-1);
        return res;
    }
};

**归并排序如果有不懂可以看看我之前写的。各类排序算法
**

离散化树状数组代码(官方)

**拖了好几天,最近学得多,唉。这个是树状数组,跟线段树还是有点差别,具体可以参考这篇博客:
树状数组
还是这个视频:
视频
讲得很清楚了,主要是要理解前缀和与lowbit之间的关系,官方这边对数组离散化处理了下,避免很多0,要维护一个很大的数组。

**
在这里插入图片描述
在这里插入图片描述

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); //这里返回的是x低位开始到第一个1以及0的数,比如88会得到8
        				//具体为什么这样去看看博客和视频
    }

    int query(int x) {
        int ret = 0;
        while (x) {
            ret += tree[x];//这里是将树状数组前缀和相加,减lowbit刚好就是前缀和
            x -= lowbit(x);
        }
        return ret;
    }

    void update(int x) {
        while (x <= n) {//只需要更新父节点的就可以,加lowbit刚好是父节点
            ++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;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/solution/shu-zu-zhong-de-ni-xu-dui-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2.总结

树状数组太神奇了。前辈们设计计算机用二进制真的是考虑很多很多,之前组成原理里面也乘除法也是,以后量子计算机就更神奇了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值