题目描述
给你一个整数数组 nums 以及两个整数 lower 和 upper 。求数组中,值位于范围 [lower, upper] (包含 lower 和 upper)之内的 区间和的个数 。
区间和 S(i, j) 表示在 nums 中,位置从 i 到 j 的元素之和,包含 i 和 j (i ≤ j)。
示例 1:
输入: nums = [-2,5,-1], lower = -2, upper = 2
输出: 3
解释: 存在三个区间:[0,0]、[2,2] 和 [0,2] ,对应的区间和分别是:-2 、-1 、2 。
示例 2:
输入: nums = [0], lower = 0, upper = 0
输出: 1
提示:
1 <= nums.length <= 105-231 <= nums[i] <= 231 - 1-105 <= lower <= upper <= 105- 题目数据保证答案是一个 32 位 的整数
前缀和数组
所谓前缀和数组,就是数组中的每一项,等于原数组中当前位置及之前元素的和值。
例如原数组为 [1,2,3,4], 前缀和数组为 [0,1,3,6,10]。 要注意的是前缀和数组中默认第一位是 0,所以前缀和数组的长度会比原数组长度 +1。
为什么要有一个前置 0呢?
- 方便计算前缀和数组的第一项的值。因为我们求前缀和数组中当前项值的时候,只需要将前一项的值加上原数组前位置的元素值即可,那么为了原数组第一个元素计算的时候有前一项值,所以需要一个前置
0。 - 方便计算区间和值。观察前缀和数组可以发现,后面的值减去前面的值,刚好等于原数组中该区间中的元素和值,即区间和值。那每一项减去最前面的
0,其实就是从原数组下标0到当前位置的区间和值(这里要注意的是,因为有一个前置0,所以前缀和数组中的下标等于原数组中的下标+1)。
解题思路
涉及到求区间和的问题,我们可以尝试使用前缀和数组来解题。
这里我们首先求得原数组的前缀和数组,拿到前缀和数组有什么用呢?我们接着往下看。
接下来,我们对前缀和数组进行 归并排序。
假设此时我们来到了归并排序的回溯过程,此时左区间有序且右区间有序。并且左区间的值的下标都小于右区间的值的下标。
又因为前缀和数组中,后面的值减去前面的值等于原数组中该区间的元素和值,也就是区间和值。所以此时如果右区间的某个元素减去左区间的某个元素,差值刚好在给定的 lower~upper 之间,则说明找到了一个符合要求的区间和值。\
动画演示
代码实现
var countRangeSum = function(nums, lower, upper) {
let s = 0;
const sum = [0];
for(const v of nums){
s += v;
sum.push(s);
}
return countRangeSumRecursive(sum,lower,upper,0,sum.length -1)
};
var countRangeSumRecursive = function(sum,lower,upper,left,right){
if(left === right){
return 0;
}else{
let mid = Math.floor((left + right)/2);
let n1 = countRangeSumRecursive(sum,lower,upper,left,mid);
let n2 = countRangeSumRecursive(sum,lower,upper,mid+1,right);
// 左右区间数量
let ret = n1 + n2;
// 首先统计一下下标的对数
let i = left;
let l = mid + 1;
let r = mid + 1;
while(i <= mid){
while(l <= right && sum[l] - sum[i] < lower) l++;
while(r <= right && sum[r] - sum[i] <= upper) r++;
ret += (r-l);
i++
}
// 合并两个排序数组
const sorted = new Array(right - left + 1);
let p1 = left, p2 = mid + 1;
let p = 0 ;
while(p1 <= mid || p2 <= right){
if(p1 > mid){
sorted[p++] = sum[p2++]
}else if( p2 > right){
sorted[p++] = sum[p1++]
}else{
if(sum[p1] < sum[p2]){
sorted[p++] = sum[p1++]
}else{
sorted[p++] = sum[p2++]
}
}
}
for(let i = 0; i < sorted.length; i++){
sum[left+i] = sorted[i];
}
return ret;
}
}
至此我们就完成了leetcode-327-区间和的个数
欢迎讨论建议