• 六月集训(25)树状数组


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

    原题链接


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

            示例 1:

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

            输出: 5

            限制:

            0 <= 数组长度 <= 50000


            这道题比较简单的方法是利用归并排序,不过本题的本意是区间查询,那么既然是区间查询树状数组自然是可以解决的,当然由于本题数据过大树状数组需要离散化,这里我们把数据离散化到1~n。

            那么该怎么查询呢?首先将nums数组放到一个临时数组tmp中,对其进行排序,然后我们从后往前进行遍历,首先找到nums数组在tmp数组的下标,然后根据下标进行查找(树状数组中求和可以看作该元素二进制减一的元素前缀和),查找完毕加入该元素,遍历完整个数组即可。

    class Solution {
        #define Max 100005
        int c[Max];
        void init(){
            memset(c,0,sizeof(c));
        }
        int low_bit(int x){
            return x&(-x);
        }
        void add(int x,int maxv,int d){
            while(x<=maxv){
                c[x]+=d;
                x+=low_bit(x);
            }
        }
        int sum(int x){
            int s=0;
            while(x>0){
                s+=c[x];
                x-=low_bit(x);
            }
            return s;
        }
        int bin(vector<int>& tmp,int v){
            int l=0,r=tmp.size()-1;
            while(l<=r){
                int mid=l+((r-l)>>1);
                if(tmp[mid]<v){
                    l=mid+1;
                }else if(tmp[mid]>v){
                    r=mid-1;
                }else {
                    return mid;
                }
            }
            return -1;
        }
    
    public:
        int reversePairs(vector<int>& nums) {
            init();
            vector<int> tmp;
            for(int i=0;i<nums.size();++i){
                tmp.push_back(nums[i]);
            }
            sort(tmp.begin(),tmp.end());
            tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
            int ans=0;
            for(int i=nums.size()-1;i>=0;--i){
                int idx=bin(tmp,nums[i])+1;
                //找到nums[i]在tmp数组的下标+1,树状数组要求下标从1开始
                ans+=sum(idx-1);
                //既然我们把数据离散化成1~n,那么求和就是树状数组中下标-1元素的前缀和了
                add(idx,nums.size(),1);
                //求和完毕加入
            }
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    2.LeetCode:493. 翻转对

    原题链接


            给定一个数组 nums ,
            如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。
    你需要返回给定数组中的重要翻转对的数量。

            示例 1:

            输入: [1,3,2,3,1]

            输出: 2

            示例 2:

            输入: [2,4,3,5,1]

            输出: 3

            注意:

            给定数组的长度不会超过50000。

            输入数组中的所有数字都在32位整数的表示范围内。


            这道题跟上一道题几乎一样,不过还是有些不同的地方。

            首先就是因为数组数据是在int32之内,但是我们查找肯定是要把数据*2的,这样就会造成数据溢出而出现错误,所以这里tmp数组我们要定义为longlong。

            具体的操作也有不同,在临时数组tmp中我们不仅要加入nums[i],还要加入nums[i]*2,其中nums[i]是我们求和的标准,nums[i]*2是我们加入到树状数组的指标也即是我们查找的标准。也就是说在求和的时候我们求取nums[i]的和,加入树状数组的时候我们加入的是nums[i]*2。

    class Solution {
        #define Max 100005
        #define ll long long
        int c[Max];
        void init(){
            memset(c,0,sizeof(c));
        }
        int low_bit(int x){
            return x&(-x);
        }
        void add(int x,int maxv,int d){
            while(x<=maxv){
                c[x]+=d;
                x+=low_bit(x);
            }
        }
        int sum(int x){
            int s=0;
            while(x){
                s+=c[x];
                x-=low_bit(x);
            }
            return s;
        }
        int bin(vector<ll>& tmp,ll v){
            int l=0,r=tmp.size()-1;
            while(l<=r){
                int mid=l+((r-l)>>1);
                if(tmp[mid]<v){
                    l=mid+1;
                }else if(tmp[mid]>v){
                    r=mid-1;
                }else {
                    return mid;
                }
            }
            return -1;
        }
    
    public:
        int reversePairs(vector<int>& nums) {
            init();
            vector<ll> tmp;
            for(int i=0;i<nums.size();++i){
                tmp.push_back(nums[i]);
                tmp.push_back((ll)nums[i]*2);
            }
            sort(tmp.begin(),tmp.end());
            tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
            ll ans=0;
            for(int i=nums.size()-1;i>=0;--i){
                int idx=bin(tmp,nums[i])+1;
                ans+=sum(idx-1);
                add(bin(tmp,(ll)nums[i]*2)+1,tmp.size(),1);
            }
            return ans;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
  • 相关阅读:
    elmentui表格修改可点击排序元素
    c语言编程请增补函数fun
    mindspore训练一段时间后,报内存不够错误
    Linux:线程安全,多线程的应用
    Effective Modern C++ 第七章 并发API 2
    简单的个人博客网站设计 静态HTML个人博客主页 DW个人网站模板下载 大学生简单个人网页作品代码 个人网页制作 学生个人网页设计作业
    springboot校园二手书交易管理系统35
    BAT034:批处理打开电脑常用功能面板
    课堂问题:一个凸函数的性质
    [附源码]计算机毕业设计网约车智能接单规划小程序Springboot程序
  • 原文地址:https://blog.csdn.net/m0_67739923/article/details/125454809