今天的题目还是看了一阵子(😅😅)
意思是任意子区间内所有元素的和要在 lower 和 upper 之间,可以取等
所以题目中 [0,0] [2,2] 都指向各自位置的元素,[0,2] 就是从第0个元素 一直加到 第2 个元素 等于 2 . 所以这就是为什么输出是3.
- 先写了个暴力:
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
int count=0;
long sum=0; //数据相加会超过int数据范围
for(int i=0;i<nums.size();++i){
if(nums[i]>=lower&&nums[i]<=upper){
count++;
}
sum=nums[i];
for(int j=i+1;j<nums.size();++j){
sum+=nums[j];
if(sum>=lower&&sum<=upper){
count++;
}
}
}
return count;
}
};
很平常的超过时间限制了。
使用前缀和依旧死得很惨。。。。
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
if(nums.size()==0)
return 0;
int count=0;
long sum=0;
vector<long> any_sum(nums.size());
any_sum[0]=nums[0];
cout<<any_sum[0]<<" ";
for(int i=1;i<nums.size();i++){
any_sum[i]=any_sum[i-1]+nums[i];
cout<<any_sum[i]<<" ";
}
// if(sum>=lower&&sum<=upper)count++;
// cout<<"\n"<<sum<<":";
// cout<<"("<<sum<<")"<<" ";
for(int i=any_sum.size()-1;i>=0;--i){
if(any_sum[i]>=lower&&any_sum[i]<=upper)count++;
for(int j=i-1;j>=0;j--){
sum=any_sum[i]-any_sum[j];
if(sum>=lower&&sum<=upper)count++;
}
}
return count;
}
};
看题解啦··············我是链接
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
int n = nums.size();
vector<int64_t> S(n+1,0);
vector<int64_t> assist(n+1,0);
for(int i=1;i<=n;i++)S[i] = S[i-1] + nums[i-1];
return merge(S,assist,0,n,lower,upper);
}
int merge(vector<int64_t> &S,vector<int64_t> &assist,int L,int R,int low,int up){
if(L >= R) return 0;
int cnt = 0;
int M = L + (R-L)/2;
cnt += merge(S,assist,L,M,low,up);
cnt += merge(S,assist,M+1,R,low,up);
int Left = L;
int Upper = M+1,Lower = M+1;
while(Left <= M){
while(Lower <= R && S[Lower] - S[Left] < low)Lower++;
while(Upper <= R && S[Upper] - S[Left] <= up)Upper++;
cnt += Upper - Lower;
Left++;
}
//以下为归并排序中归并过程
Left = L;
int Right = M + 1;mmp
int pos = L;
while(Left<= M || Right <= R){
if(Left > M)assist[pos] = S[Right++];
if(Right > R && Left <= M)assist[pos] = S[Left++];
if(Left <= M && Right <= R){
if(S[Left] <= S[Right])assist[pos] = S[Left++];
else assist[pos] = S[Right++];
}
pos++;
}
for(int i=L;i<=R;i++)S[i] = assist[i];
return cnt;
}
};
作者的意思是,利用 前缀和,归并排序
1.前缀和先求出所有的相应位置的和
2.归并排序会把数组分成若干区间,让后两两相比较排序,从底向上的排序。
3.根据题目要求 成立条件就变成了:lower<=s[i]-s[j]<=upper
作者作图表示的是归并最后一层
这一块是关键。
根据最后的成立条件:lower<=s[i]-s[j]<=upper ( i >j )
只要分组后的左边某个之和作为基准
右边能够使得
- s[i0]-s[j]>=lower
- s[i1]-s[j]<=upper
那为什么 成立条件集合是这个家伙呢????
cnt += Upper - Lower
首先我们知道这是被排过序了(单看一边)的吧(图中是最后一层归并了)
回想一下只使用前缀和时,我们在左边确定一个锚点,从锚点的下一个位置开始一个一个的减,若是满足 lower<=sum<=upper 咱们就加上。
在归并中 ,我们直接确定 当 left=i 时 有序数组下 最小的满足 >=lower 的位置 和 最大满足 <=upper 的位置,两者相减得到的长度表示,该区间内的元素都满足 lower<= sum<=upper
好吧,剩下的就时归并排序的样子了,多敲敲就行。