leetcode算法题327
链接:https://leetcode.cn/problems/count-of-range-sum
给你一个整数数组 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
主要是利用归并排序的思路,在拆、合的过程中,进行计算
public static int countRangeSum(int[] nums, int lower, int upper) {
if (nums == null || nums.length == 0) {
return 0;
}
int N = nums.length;
// 前缀和数组,方便你求区间[1,3]的和,直接使用[0,3]-[0,1],而不用提前遍历很多次,生成好各种区间的和
long[] preSum = new long[N + 1];
preSum[0] = nums[0];
for (int i = 1; i < N; i++) {
// 当前位置的累加和=之前位置累加和+当前位置值
preSum[i] = preSum[i - 1] + nums[i];
}
return countRangeSumChai(preSum, 0, N - 1, lower, upper);
}
private static int countRangeSumChai(long[] preSum, int left, int right, int lower, int upper) {
if (left == right) {
if (preSum[left] >= lower && preSum[left] <= upper) {
return 1;
}
return 0;
}
int mid = (left + right) / 2;
return countRangeSumChai(preSum, left, mid, lower, upper) + countRangeSumChai(preSum, mid + 1, right, lower, upper) + countRangeSumHe(preSum, left, mid, right, lower, upper);
}
private static int countRangeSumHe(long[] preSum, int l, int mid, int r, int lower, int upper) {
int res = 0;
/**
* 假设lower=10,upper=40,前缀和[0,3]=100,求以3下标结尾的数组,有多个累加和,满足[10,40]
* 假设前缀和arr[1,3]满足,那么前缀和arr[0,3]-arr[0,1]也满足,这就要求arr[0,1]要满足[100-40,100-10]=[60,90]
*/
// 关键点:
// 以i下标结尾的数组,有多少个前缀和满足
// 这种写法会超出时间
// for (int i = mid+1; i <= r; i++) {
// long min = preSum[i] - upper;
// long max = preSum[i] - lower;
// for (int j = l; j <= mid; j++) {
// if(preSum[j] >= min && preSum[j]<=max){
// res++;
// }
// }
// }
// 其实就是某个区间满足[lower,upper],不进行merge前,因为是有序的,再往前或往后就超出这个区间了
int windowL = l;
int windowR = l;
for (int i = mid + 1; i <= r; i++) {
long min = preSum[i] - upper;
long max = preSum[i] - lower;
while (windowL <= mid && preSum[windowL] < min) {
windowL++;
}
while (windowR <= mid && preSum[windowR] <= max) {
windowR++;
}
res += windowR - windowL;
}
long[] helps = new long[r - l + 1];
int p1 = l;
int p2 = mid + 1;
int curIndex = 0;
while (p1 <= mid && p2 <= r) {
helps[curIndex++] = preSum[p1] < preSum[p2] ? preSum[p1++] : preSum[p2++];
}
while (p1 <= mid) {
helps[curIndex++] = preSum[p1++];
}
while (p2 <= r) {
helps[curIndex++] = preSum[p2++];
}
for (int i = 0; i < helps.length; i++) {
preSum[l + i] = helps[i];
}
return res;
}