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;
}