327. 区间和的个数

 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。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-of-range-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

基本思路:利用multiset是有序数组,在每次插入元素之前,都要查询所有以该元素为为节点的符合条件的元素的数量

  • lower<=s[j]-s[i]<=upper
  • s[j]-upper<=s[i]<=s[j]-lower ,0<=i<j<s.size();
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        int n= nums.size();
        int64_t presum = 0;
        multiset<int64_t> S;
        S.insert(0);  //致使下面的操作包括自身元素。
        int ret = 0;
        for(int i=0;i<n;i++){
            presum += nums[i];
            ret += distance(S.lower_bound(presum-upper),S.upper_bound(presum-lower));
            S.insert(presum);
        }
        return ret;
    }

基本思路:采用归并排序,每一次有两个有序数组,以左边数组里的元素为左边界,在右边数组中找到符合要求的最大元素数目

  • 将前缀和数组分为两个有序数组,即左右分别有序数组,s1,s2
  • 以s1[i],0<=i<s1.size(),为左边界,在s2上找符合要求的所有元素,s2[j]-s1[i]>=lower\bigcap s2[j]-s1[i]<=upper
  • 递归以上步骤。

 

    vector<long> tmp;
    int merge(vector<long> &sum,int l,int r,int &lower,int &upper){
        if(r<=l)
            return 0; //若前缀和数组从一开始,仅需要改变该处,针对单个元素,return sum[l]>=lower&&sum[l]<=upper;
        //计算所有符合要求的自序列个数
        int cnt=0;
        int mid=(r-l)/2+l;
        cnt+=merge(sum,l,mid,lower,upper);//保证右边的数组下标一定大于左边的数组
        cnt+=merge(sum,mid+1,r,lower,upper);
        int left_pos=l,right_lower=mid+1,right_upper=mid+1;
        while(left_pos<=mid){
            while(right_lower<=r&&sum[right_lower]-sum[left_pos]<lower)
                right_lower++;
            while(right_upper<=r&&sum[right_upper]-sum[left_pos]<=upper)
                right_upper++;
            cnt+=right_upper-right_lower;
            left_pos++;
        }
        // 归并
        int left=l;
        int right=mid+1;
        int pos=l;
        while(left<=mid||right<=r){
            if(left<=mid&&right<=r){
                if(sum[left]<=sum[right]){
                    tmp[pos]=sum[left++];
                }
                else{
                    tmp[pos]=sum[right++];
                }
            }
            else if(left>mid){
                tmp[pos]=sum[right++];
            }
            else
                tmp[pos]=sum[left++];

            pos++;
        }

        for(int i=l;i<=r;i++){
            sum[i]=tmp[i];
        }
        return cnt;

    }
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        vector<long> sum(nums.size()+1,0);
        tmp.resize(nums.size()+1,0);
        for(int i=0;i<nums.size();i++){
            sum[i+1]=sum[i]+nums[i];
        }
        return merge(sum,0,nums.size(),lower,upper); //从零开始,保证包含数组单个元素自身。

    }

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值