区间和的个数
题目描述
给你一个整数数组 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
<
=
n
u
m
s
.
l
e
n
g
t
h
<
=
1
0
5
1 <= nums.length <= 10^{5}
1<=nums.length<=105
− 2 31 = n u m s [ i ] < = 2 31 − 1 -2^{31} = nums[i] <= 2^{31}-1 −231=nums[i]<=231−1
− 1 0 5 < = l o w e r < = u p p e r < = 1 0 5 -10^{5} <= lower <= upper <= 10^{5} −105<=lower<=upper<=105
- 题目数据保证答案是一个 32 位 的整数
题目分析
1、区间和,想到前缀和
2、区间和表示的是两两前缀和相减,然后判断是否满足区间。正常来做的化是双层for循环。但是数据规模给的是10的5次幂,因此算法的时间复杂度只能为O(nlogn)。因此作比较的就只能将时间复杂度缩写,归并排序的方式正好可以满足。
代码
public int countRangeSum(int[] nums, int lower, int upper) {
int len = nums.length;
if(nums == null || nums.length<1){
return -1;
}
long[] sum = new long[len];
sum[0] = nums[0];
for (int i = 1; i < len; i++) {
sum[i] =sum[i-1]+nums[i];
}
int result = process(sum,0,len-1,lower,upper);
return result;
}
private int process(long[] sum, int L, int R, int lower, int upper) {
if(L==R){
return sum[L] >=lower && sum[R] <= 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);
}
private int merge(long[] sum, int l, int m, int r, int lower, int upper) {
int WindowL = l;
int WindowR = l;
int index = m+1;
int ans = 0;
while (index<=r){
long max = sum[index]-lower;
long min = sum[index]-upper;
while(WindowR<=m && sum[WindowR]<=max){
WindowR++;
}
while (WindowL<= m && sum[WindowL]<min){
WindowL++;
}
ans += WindowR-WindowL;
index++;
}
int p1= l;
int p2 = m+1;
long[] help = new long[r-l+1];
int cur = 0;
while (p1<= m && p2<=r){
help[cur++] = sum[p1]<sum[p2]?sum[p1++]:sum[p2++];
}
while (p1<=m){
help[cur++] = sum[p1++];
}
while (p2<=r){
help[cur++] = sum[p2++];
}
for (int i = 0; i < help.length; i++) {
sum[l+i] = help[i];
}
return ans;
}
总结
- 归并排序步骤:结束条件
L=R
,从左、右、合并中获取所需结果 - 别忘记中间每个过程,左右都是已经排好序了的