题目来源
题目描述
题目解析
1.使用前缀和代替原数组;
2.在归并排序合并方法内,统计满足条件的累加和个数 和 合并操作分开。
3.每次合并操作,对于右组(前缀和数组)中的每一个数X[ i ],求左组(前缀和数组)所有数中有多少个范围在 [X[ i ] - upper, X[ i ] - lower]上,将满足条件的个数累加起来即为最后的结果
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
if(nums.empty()){
return 0;
}
int size = nums.size();
std::vector<long> sum(size);
sum[0] = nums[0];
for (int i = 1; i < size; ++i) {
sum[i] = sum[i - 1] + nums[i];
}
return process(sum, 0, sum.size() - 1, lower, upper);
}
int process(std::vector<long> & sum, int L, int R, int lower, int upper){
if(L == R){
return sum[L] >= lower && sum[L] <= upper ? 1 : 0;
}
int M = L + (R - L) >> 1;
return process(sum, L, M, lower, upper) + process(sum, M + 1, R, lower, upper)
+ merge(sum, L, M, R, lower, upper);
}
int merge(std::vector<long> & arr, int L, int M, int R, int lower, int upper){
int ans = 0;
int windowL = L; // 左组寻找的左侧位置(肯定是从当前的L位置开始)
int windowR = L; // 左组寻找的右侧位置(肯定也是从当前的L位置开始)
// 对于右组的每一个数X,在左组中寻找值在[X-upper, X-lower]之间的个数
for (int i = M + 1; i <= R; ++i) {
long min = arr[i] - upper;
long max = arr[i] - lower;
// 因为是在左组中寻找,所以下标不能超过mid
// 寻找当前值大于等于min的第一个位置(因为等于min的时候没有右移,所以包含此位置)
while (windowL <= M && arr[windowL] < min) {
windowL++;
}
// 寻找当前值比max大的第一个位置(因为等于max的时候右移了一位,所以不包含此位置)
while (windowR <= M && arr[windowR] <= max) {
windowR++;
}
// 最后满足要求的累加和个数为 [windowL, windowR),即 windowR - windowL,windowR是开区间,所以不 +1
ans += windowR - windowL;
}
// 以下是经典的merge过程
std::vector<int> help(R - L + 1);
int p1 = L, p2 = M + 1, i = 0;
while (p1 <= M && p2 <= R){
help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= M){
help[i++] = arr[p1++];
}
while (p2 <= R){
help[i++] = arr[p2++];
}
i = 0;
for (int j = L; j <= R; ++j) {
arr[j] = help[i++];
}
return ans;
}
};
类似题目
-
leetcode:315. 计算右侧小于当前元素的个数 Count of Smaller Numbers After Self
-
leetcode:304. 二维区间和检索 - 矩阵不可变 Range Sum Query 2D - Immutable
区间和计数