Count of Range Sum(leetcode)

Count of Range Sum


题目

leetcode题目
Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.
Range sum S(i, j) is defined as the sum of the elements in nums between indices i and j (i ≤ j), inclusive.

Note:
A naive algorithm of O( n2 ) is trivial. You MUST do better than that.

Example:
Given nums = [-2, 5, -1], lower = -2, upper = 2,
Return 3.
The three ranges are : [0, 0], [2, 2], [0, 2] and their respective sums are: -2, -1, 2.


题目解析

题目的要求就是求原数组的子数组个数,子数组满足“子数组的数之和在给定的范围[lower, upper]内”条件。
例子中,nums = [-2, 5, -1],子数组为

  1. [-2] => [0, 0]
  2. [5] => [1, 1]
  3. [-1] => [2, 2]
  4. [-2, 5] => [0, 1]
  5. [5, -1] => [1, 2]
  6. [-2, 5, -1] => [0, 2]

子数组的总和在[-2, 2]中的子数组为[-2][-1][-2, 5, -1],个数为3。


解决办法

1. sum数数

sums[i] = nums[0] + nums[1] + … + nums[i]
找到满足条件lower <= sums[j] - sums[i] <= upper的区间[i, j](i <= j),即我们需要找到满足sums[j] - upper <= sums[i] <= sums[j] - loweri的个数。
PS:函数distance(iterator start, iterator end),迭代器start可以通过某种方法到达end,函数distance返回的是start到end的个数。

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

2. 归并排序

将数组sums分为两部分:左半部left和右半部right。由于数组sums在构建的时候已经呈递增顺序,使得leftright已经排好序了。当我们遍历left时候,在right找到满足以下条件的j, k

  1. sums[j] - sums[i] > upperj是满足该不等式的第一个下标
  2. sums[k] - sums[i] >= lowerk是满足该不等式的第一个下标

那么在[lower, upper]之间的区间的个数是j - k

同时我们也需要另一个下标t,用来拷贝所有满足sums[t] < sums[i]到一个寄存器Cache中以完成混合排序的过程。

class Solution {
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        int s = nums.size();
        vector<long> sums(s + 1, 0);
        for (int i = 0; i < nums.size(); ++i) {
            sums[i + 1] = sums[i] + nums[i];
        }
        return countAndMergeSort(sums, 0, sums.size(), lower, upper);
    }
    int countAndMergeSort(vector<long> &sums, int start, int end, int lower, int upper) {
        if (end - start <= 1) return 0;
        int mid = start + (end - start) / 2;
        int count = countAndMergeSort(sums, start, mid, lower, upper) + countAndMergeSort(sums, mid, end, lower, upper);
        int j = mid;
        int k = mid;
        int t = mid;
        vector<int> cache(end - start, 0);
        for (int i = start, r = 0; i < mid; i++, r++) {
            while (k < end && sums[k] - sums[i] < lower) k++;
            while (j < end && sums[j] - sums[i] <= upper) j++;
            while (t < end && sums[t] < sums[i]) cache[r++] = sums[t++];
            cache[r] = sums[i];
            count += j - k;
        }
        copy(cache.begin(), cache.begin() + t - start, sums.begin() + start);
        return count;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值