给定一个整数数组 nums,返回区间和在 [lower, upper] 之间的个数,包含 lower 和 upper。
区间和 S(i, j) 表示在 nums 中,位置从 i 到 j 的元素之和,包含 i 和 j (i ≤ j)。
说明:
最直观的算法复杂度是 O(n2) ,请在此基础上优化你的算法。
示例:
输入: nums = [-2,5,-1], lower = -2, upper = 2,
输出: 3
解释: 3个区间分别是: [0,0], [2,2], [0,2],它们表示的和分别为: -2, -1, 2。
线段树+暴力(超时)
就是想写一个线段树,完全没有用到;
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
buildTree(nums);
int cnt=0;
for(int i=0;i<n;i++)
for(int j=i;j<n;j++){
long long temp=rangesum(i,j);
if(temp>=lower&&temp<=upper)
cnt++;
}
return cnt;
}
private:
vector<long long> Tree;
int n;
void buildTree(vector<int>& nums){
n=nums.size();
Tree.resize(2*n,0);
for(int i=n;i<2*n;i++)
Tree[i]=nums[i-n];
for(int i=n-1;i>0;i--)
Tree[i]=Tree[i*2]+Tree[i*2+1];
}
long long rangesum(int a,int b){ //从i到j
int start=a+n; //起
int end=b+n; //终
long long res=0;
while(start<=end){
if(start%2){
res+=Tree[start];
start++;
}
if(end%2==0){
res+=Tree[end];
end--;
}
start/=2;
end/=2;
}
return res;
}
};
使用multiset结构,将已计算出的presum排序;(使用平衡树);
每次得到当前presum-upper在multiset中的位置,以及presum-lower在multiset中的位置;(lower_bound和upper_bound保证重复元素也计算在内);
将新的presum加入multiset;
使用distance函数可以找到位置只差;
注意点
multiset中需要先插入0,让先入元素判断自身是否满足要求;
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
presum=0;
int n=nums.size();
S.insert(0);
int res=0;
for(int i=0;i<n;i++){
presum+=nums[i];
res+=distance(S.lower_bound(presum-upper),S.upper_bound(presum-lower));
S.insert(presum);
}
return res;
}
private:
long long presum;
multiset<long long> S;
};
类似归并排序,先算出前缀和,对前缀和进行归并排序的同时,统计后部分-前部分前缀和,满足lower~upper之间的个数;
注意点
inplace_merge函数为【)【)区间;故当end取闭区间时,需要+1;
end为闭区间的归并sort
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
this->lower=lower;
this->upper=upper;
int n=nums.size();
sums.resize(n+1,0);
for(int i=1;i<=n;i++)
sums[i]=sums[i-1]+nums[i-1];
return merge(sums,0,n);
}
private:
vector<long long> sums;
int lower,upper;
int merge(vector<long long>& sums,int start,int end){
if(end-start<=0) return 0; //
int mid=start+(end-start)/2; //靠左
int leftnum=merge(sums,start,mid);
int rightnum=merge(sums,mid+1,end);
int result=merge_sort(sums,start,mid,end);
return leftnum+rightnum+result;
}
int merge_sort(vector<long long>& sums,int start,int mid,int end){
//start~mid mid+1~end
int resbegin=mid+1;
int resend=mid+1;
int res=0;
for(int i=start;i<=mid;i++){ //两边为已排序好的前缀和,找到
while(resbegin<=end&&sums[resbegin]-sums[i]<lower) resbegin++; //找到第一个大于的地方
while(resend<=end&&sums[resend]-sums[i]<=upper) resend++; //找到第一个大于的地方
res+=resend-resbegin;
}
inplace_merge(sums.begin()+start,sums.begin()+mid+1,sums.begin()+end+1);
return res;
}
};
end为开区间的归并sort
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
this->lower=lower;
this->upper=upper;
int n=nums.size();
sums.resize(n+1,0);
for(int i=1;i<=n;i++)
sums[i]=sums[i-1]+nums[i-1];
return merge(sums,0,n+1);
}
private:
vector<long long> sums;
int lower,upper;
int merge(vector<long long>& sums,int start,int end){
if(end-start<=1) return 0; //
int mid=start+(end-start)/2; //靠左
int leftnum=merge(sums,start,mid);
int rightnum=merge(sums,mid,end);
int result=merge_sort(sums,start,mid,end);
return leftnum+rightnum+result;
}
int merge_sort(vector<long long>& sums,int start,int mid,int end){
//start~mid mid+1~end
int resbegin=mid;
int resend=mid;
int res=0;
for(int i=start;i<mid;i++){ //两边为已排序好的前缀和,找到
while(resbegin<end&&sums[resbegin]-sums[i]<lower) resbegin++; //找到第一个大于的地方
while(resend<end&&sums[resend]-sums[i]<=upper) resend++; //找到第一个大于的地方
res+=resend-resbegin;
}
inplace_merge(sums.begin()+start,sums.begin()+mid,sums.begin()+end);
return res;
}
};