题目描述
Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.
Range sum S(i, j) is defined as the sum of the elements in nums between indices i and j (i
≤
j), inclusive.
Note:
A naive algorithm of O(
n2
) is trivial. You MUST do better than that.
Example:
Given nums = [-2, 5, -1], lower = -2, upper = 2,
Return 3.
The three ranges are : [0, 0], [2, 2], [0, 2] and their respective sums are: -2, -1, 2.
Credits:
Special thanks to @dietpepsi for adding this problem and creating all test cases.
Link:
https://leetcode.com/problems/count-of-range-sum/description/
解决方案
本题是求数组的区间和在给定范围内的个数。数组的区间和s(i,j)指的是索引在i与j(i
≤
j)之间的所有元素的和,给定范围在[lower,upper]之间,求出所有满足lower
≤
s(i,j)
≤
upper的区间[i,j]的个数。
由之前类似的题目Range Sum Query - Immutable,Range Sum Query 2D - Immutable 和 Range Sum Query - Mutable,求区间和的问题一般是先计算累加和sums[i]=nums[0]+nums[1]+ ··· +nums[i],则s(i,j)=sums[j]-sums[i]。
通过参考资料,解决该题的基本思想是:
- 对累加和数组的某个元素
sums[i],对其之后(或之前)的元素进行排序。 - 对于有序序列sorted[start,end],找出满足
sorted[j]≥lower的第一个sorted[j]和满足sorted[k]>upper的第一个sorted[k],则k-j即为数组中在[lower,upper]范围内的所有元素的个数。
下面是两种解法,其中解法一被使用和引用的最多。
解法一
采用“归并排序(Merge Sort)”来求解,基本思路是将累加和数组sums[start,end]分为左右两半,其中左半部[start,mid)和右半部[mid,end)先分别递归调用解法一求出各自的满足条件的区间和个数,并完成各自部分的排序。然后再将已经排好序的左右两部进行归并,完成整体排序。在整体排序过程中,遍历左半部分的sums[i],找出右半部分的sums[j]和sums[k],使其满足:
sums[j]是第一个满足sums[j]− sums[i]≥lower的元素。sums[k]是第一个满足sums[k]−sums[i]> upper的元素。
则k-j便是在[lower,upper]之间的区间和的个数。因为采用自底向上的归并排序,所以保证了对每一个sums[i],能够找到其后所有满足条件的sums[j]和sums[k]。
在整体排序的过程中,还需要一个缓存数组cache来存储每次遍历i时的左右半排序结果,通过复制完成sums[start,end]间的整体排序。
以下为源码:
//C++ Merge Sort
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
int len=nums.size();
vector<long long> sums(len+1,0);
for(int i=0;i<len;i++){
sums[i+1]=sums[i]+nums[i];
}
return countAndMergeSort(sums,0,len+1,lower,upper);
}
int countAndMergeSort(vector<long long>& sums,int start,int end,int lower,int upper){
if(end-start<=1)return 0;
vector<long long> cache(end-start);
int middle=(start+end)/2;
int cnt=countAndMergeSort(sums,start,middle,lower,upper)+
countAndMergeSort(sums,middle,end,lower,upper);
int j=middle,k=middle,t=middle;
int r=0;
for(int i=start;i<middle;++i){
while(j<end && sums[j]-sums[i]<lower)j++;
while(k<end && sums[k]-sums[i]<=upper)k++;
while(t<end && sums[t]<sums[i])cache[r++]=sums[t++];
cache[r++]=sums[i];
cnt+=k-j;
}
copy(cache.begin(),cache.begin()+r,sums.begin()+start);
return cnt;
}
};
解法二
使用multiset,遍历nums[i],逐一将i位置的累加和插入到multiset中,保证已经插入的累加和有序。对于位置i,multiset中已经有按序排列的前i个累加和。目标是找到i前的满足条件lower
≤
sums[i]
−
sums[j] upper 的sums[j]的个数,即寻找满足条件 sums[i]
−
upper sums[j]
≤
sums[i]
−
lower 的两个临界值之间的元素个数。临界值可以使用lower_bound(返回有序集合中大于等于给定值的第一个数)和upper_bound(返回有序集合中大于给定值的第一个数)来求得。
以下为源码:
//C++ multiset
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
multiset<long long> sums;
sums.insert(0);
int res=0;
long long sum=0;
for(int i=0;i<nums.size();i++){
sum+=nums[i];
res+=distance(sums.lower_bound(sum-upper),sums.upper_bound(sum-lower));
sums.insert(sum);
}
return res;
}
};
相关题目
LeetCode_303 Range Sum Query - Immutable
LeetCode_304 Range Sum Query 2D - Immutable
LeetCode_307 Range Sum Query - Mutable
参考资料
https://leetcode.com/problems/count-of-range-sum/discuss/
https://www.cnblogs.com/grandyang/p/5162678.html

本文介绍了一种高效的算法,用于计算数组中区间和落在指定范围内的数量。通过使用归并排序和multiset数据结构,实现了优于朴素O(n^2)的时间复杂度。
448






