multiset(平衡树)+分治(归并) 327. 区间和的个数

36 篇文章 0 订阅
13 篇文章 0 订阅

327. 区间和的个数

给定一个整数数组 nums,返回区间和在 [lower, upper] 之间的个数,包含 lower 和 upper。
区间和 S(i, j) 表示在 nums 中,位置从 i 到 j 的元素之和,包含 i 和 j (i ≤ j)。

说明:
最直观的算法复杂度是 O(n2) ,请在此基础上优化你的算法。

示例:

输入: nums = [-2,5,-1], lower = -2, upper = 2,
输出: 3 
解释: 3个区间分别是: [0,0], [2,2], [0,2],它们表示的和分别为: -2, -1, 2

线段树+暴力(超时)
就是想写一个线段树,完全没有用到;

class Solution {
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        buildTree(nums);
        int cnt=0;
        for(int i=0;i<n;i++)
            for(int j=i;j<n;j++){
                long long temp=rangesum(i,j);
                if(temp>=lower&&temp<=upper)
                       cnt++; 
            }
        return cnt;
    }
private:
    vector<long long> Tree;
    int n;
    void buildTree(vector<int>& nums){
        n=nums.size();
        Tree.resize(2*n,0);
        for(int i=n;i<2*n;i++)
            Tree[i]=nums[i-n];
        for(int i=n-1;i>0;i--)
            Tree[i]=Tree[i*2]+Tree[i*2+1];
    }
    long long rangesum(int a,int b){  //从i到j
        int start=a+n;  //起
        int end=b+n;  //终
        long long res=0;
        while(start<=end){
            if(start%2){
                res+=Tree[start];
                start++;
            }
            if(end%2==0){
                res+=Tree[end];
                end--;
            }
            start/=2;
            end/=2;
        }
        return res;
    }
};

平衡树(ac)

使用multiset结构,将已计算出的presum排序;(使用平衡树);
每次得到当前presum-upper在multiset中的位置,以及presum-lower在multiset中的位置;(lower_bound和upper_bound保证重复元素也计算在内);
将新的presum加入multiset;
使用distance函数可以找到位置只差;

注意点
multiset中需要先插入0,让先入元素判断自身是否满足要求;

class Solution {
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        presum=0;
        int n=nums.size();
        S.insert(0);
        int res=0;
        for(int i=0;i<n;i++){
            presum+=nums[i];
            res+=distance(S.lower_bound(presum-upper),S.upper_bound(presum-lower));
            S.insert(presum);
        }    
        return res;
    }
private:
    long long presum;
    multiset<long long> S; 
};

分治(ac)

类似归并排序,先算出前缀和,对前缀和进行归并排序的同时,统计后部分-前部分前缀和,满足lower~upper之间的个数;
注意点
inplace_merge函数为【)【)区间;故当end取闭区间时,需要+1;

end为闭区间的归并sort

class Solution {
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        this->lower=lower;
        this->upper=upper; 
        int n=nums.size();
        sums.resize(n+1,0);
        for(int i=1;i<=n;i++)
            sums[i]=sums[i-1]+nums[i-1];
        return merge(sums,0,n);
    }
private:
    vector<long long> sums;
    int lower,upper;
    int merge(vector<long long>& sums,int start,int end){
        if(end-start<=0) return 0;  //
        int mid=start+(end-start)/2;  //靠左
        int leftnum=merge(sums,start,mid);
        int rightnum=merge(sums,mid+1,end);
        int result=merge_sort(sums,start,mid,end);
        return leftnum+rightnum+result;
    }

    int merge_sort(vector<long long>& sums,int start,int mid,int end){
        //start~mid  mid+1~end   
        int resbegin=mid+1;
        int resend=mid+1;
        int res=0;
        for(int i=start;i<=mid;i++){  //两边为已排序好的前缀和,找到
            while(resbegin<=end&&sums[resbegin]-sums[i]<lower) resbegin++; //找到第一个大于的地方
            while(resend<=end&&sums[resend]-sums[i]<=upper) resend++; //找到第一个大于的地方
            res+=resend-resbegin;
        }
        inplace_merge(sums.begin()+start,sums.begin()+mid+1,sums.begin()+end+1);
        return res;
    }
};

end为开区间的归并sort

class Solution {
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        this->lower=lower;
        this->upper=upper; 
        int n=nums.size();
        sums.resize(n+1,0);
        for(int i=1;i<=n;i++)
            sums[i]=sums[i-1]+nums[i-1];
        return merge(sums,0,n+1);
    }
private:
    vector<long long> sums;
    int lower,upper;
    int merge(vector<long long>& sums,int start,int end){
        if(end-start<=1) return 0;  //
        int mid=start+(end-start)/2;  //靠左
        int leftnum=merge(sums,start,mid);
        int rightnum=merge(sums,mid,end);
        int result=merge_sort(sums,start,mid,end);
        return leftnum+rightnum+result;
    }

    int merge_sort(vector<long long>& sums,int start,int mid,int end){
        //start~mid  mid+1~end   
        int resbegin=mid;
        int resend=mid;
        int res=0;
        for(int i=start;i<mid;i++){  //两边为已排序好的前缀和,找到
            while(resbegin<end&&sums[resbegin]-sums[i]<lower) resbegin++; //找到第一个大于的地方
            while(resend<end&&sums[resend]-sums[i]<=upper) resend++; //找到第一个大于的地方
            res+=resend-resbegin;
        }
        inplace_merge(sums.begin()+start,sums.begin()+mid,sums.begin()+end);
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值