「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」
327. 区间和的个数
给你一个整数数组 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 位 的整数
归并排序
思路
要求区间和,就是吧从i到j之间的值相加(i<=j)
怎么用归并呢,排序数组不久乱了?
这里我们可以使用归并排序,但是区间和怎么能和区间和产生关系呢?区间和是原数组中某一段元素的和,如果排序那么数据就会发生混乱,得不到原来的区间和。
这里我们可以巧妙的运用前缀和,将从i到j的区间的和保存在新的前缀和数组的j位(准确的说是j+1位),这样我们就可以通过区间和的第j位减去第i位得到原数组中i+1到j之间的区间和。
因为区间和的元素可以为1位,那么要求首位的值的话我们需要在区间和0位放置一个数字0,那么原数组中的[0,1] [0,1] [0,..j]就可以通过前缀和的第j位减去第0位求得,所以准确来说是保存在第j+1位中
怎么能快速得到满足条件的区间和数量呢?
前缀和的所有元素就可以不用在乎位置而随意排序,变成有序数组那么再求[lower,upper]就会快很多,因为无需全部遍历,在归并过程中我们会得到两个有序数组sums1和sums2,我们用i标记sums1的首位,l和r标记sums2的首位,遍历sums1中的每一位和sums2中的每一位作差(sums2[l]-sums1[i])
- 如果结果小于lowwer就不符合题意,继续l++,直到差结果大于等于lowwer,那么从l开始就是一个满足于题意的区间和
- r>=l,只要r在有效下标以内,那么r继续往后移动,sums[r]-sums[i]如果小于upper那么继续移动r++,直到sums的最后一位
- 此时[l,r)减去i都是满足题意的结果,一共r-l次
具体实现:
- 求出前缀和进行归并排序
- 将前缀和从中间一分为二,递归处理left和right部分,递归终止条件为分割成1位元素时自然是有序的,直接返回本身以及满足条件的总次数0
- 依照上面思路求两个有序序列之间满足条件的数量
- 最后进行排序,排序结束则得到最终答案
var countRangeSum = function (nums, lower, upper) {
//求区间和联想到前缀和前缀和
//区间和 S(i, j) 表示在 nums 中,位置从 i 到 j 的元素之和,包含 i 和 j (i ≤ j)。
//那么在前缀和sums中,可以用sums[j]-sums[i-1](因为包含了i和j)来表示区间和S(i,j)。所以为了表达端点需要在前缀和初始值增加一个0.
var sums = [0]
var total = 0
for (var i = 0; i < nums.length; i++) {
sums.push(total += nums[i])
}
const result = computedS(sums, lower, upper)
return result[1]
};
var computedS = function (sums, lower, upper) {
if (sums.length < 2) return [sums, 0]
var mid = Math.floor((sums.length) / 2);
var left = sums.slice(0, mid)
var right = sums.slice(mid)
var [sums1, n1] = computedS(left, lower, upper)
var [sums2, n2] = computedS(right, lower, upper)
var res = n1 + n2
// 声明l和r指向右侧数组的第一项
var i = 0;
var l = r = 0
// 循环统计
while (i <= mid - 1) {
while (l <= sums2.length - 1 && sums2[l] - sums1[i] < lower) l++
while (r <= sums2.length - 1 && sums2[r] - sums1[i] <= upper) r++
res += (r - l)
i++
}
var arr = []
var p1 = p2 = 0
var index = 0
while (p1<sums1.length || p2<sums2.length) {
if (p1 >= sums1.length) {
arr[index++] = sums2[p2++]
} else if (p2 >= sums2.length) {
arr[index++] = sums1[p1++]
}else if (sums1[p1] < sums2[p2]) {
arr[index++] = sums1[p1++]
} else {
arr[index++] = sums2[p2++]
}
}
return [arr, res]
}
复制代码