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]
,子数组为
[-2]
=>[0, 0]
[5]
=>[1, 1]
[-1]
=>[2, 2]
[-2, 5]
=>[0, 1]
[5, -1]
=>[1, 2]
[-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] - lower
的i
的个数。
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
在构建的时候已经呈递增顺序,使得left
和right
已经排好序了。当我们遍历left
时候,在right
找到满足以下条件的j, k
:
sums[j] - sums[i] > upper
,j
是满足该不等式的第一个下标sums[k] - sums[i] >= lower
,k
是满足该不等式的第一个下标
那么在[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;
}
};