题目描述:
给定一个整数数组 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。
方法1:
主要思路:
(1)暴力法,超时;
(2)先计算前缀和,再使用O(n2)的时间复杂度,将判断每一种情形;
class Solution {
public:
int countRangeSum(vector<int>& nums, int lower, int upper) {
if(nums.empty()){//处理特殊的情形
return 0;
}
//计算数组的前缀和
vector<long long> sum_sub(nums.size(),0);
sum_sub[0]=nums[0];
for(int i=1;i<nums.size();++i){
sum_sub[i]=nums[i]+sum_sub[i-1];
}
int res=0;
//判断每一种情形是否符合要求
for(int i=0;i<nums.size();++i){
for(int j=i;j<nums.size();++j){
long long tmp=sum_sub[j]-sum_sub[i]+nums[i];
if(tmp>=lower&&tmp<=upper){
++res;
}
}
}
return res;
}
};
方法2:
主要思路:
(1)归并排序;
(2)前面的思路一致,还是使用前缀和;
(3)归并的过程中,判断左右范围可以组成的满足要求范围,这里之所以能够使用右边的减去左边的进行判断,是因为右边的在合并之前,一定是在左边的右边,既一定能相减,形成一个区间;
(4)再考虑到左右的各自部分都是有序的,故可以加快判断有效的范围;
class Solution {
public:
void merge_find(vector<long long>& sum_sub,vector<long long>& tmp,int left,int right,int lower,int upper,int& res){
if(left==right){//归并的范围降低到1个元素
if(sum_sub[left]>=lower&&sum_sub[right]<=upper){
++res;
}
return;
}
int mid=left+(right-left)/2;//递归的进行归并判断
merge_find(sum_sub,tmp,left,mid,lower,upper,res);
merge_find(sum_sub,tmp,mid+1,right,lower,upper,res);
//找出满足要求的范围
int i=left;
int j=mid+1;
int k=mid+1;
while(i<=mid){//以左部分中的各个位置作为范围的起始
//判断右边部分中,可以作为范围的终止的位置
while(j<=right&&sum_sub[j]-sum_sub[i]<lower){
++j;
}
k=j;//判断上区间,满足要求,就统计
while(k<=right&&sum_sub[k]-sum_sub[i]<=upper){
++res;
++k;
}
++i;
}
//合并左右有序的部分
i=left;
j=mid+1;
k=left;
while(i<=mid&&j<=right){
if(sum_sub[i]<=sum_sub[j]){
tmp[k++]=sum_sub[i++];
}
else{
tmp[k++]=sum_sub[j++];
}
}
//处理没有合并完的部分
while(i<=mid){
tmp[k++]=sum_sub[i++];
}
while(j<=right){
tmp[k++]=sum_sub[j++];
}
//赋值到原始数组
while(left<=right){
sum_sub[left]=tmp[left];
++left;
}
}
int countRangeSum(vector<int>& nums, int lower, int upper) {
if(nums.empty()){//处理特殊的情形
return 0;
}
//前缀和
vector<long long> sum_sub(nums.size(),0);
sum_sub[0]=nums[0];
for(int i=1;i<nums.size();++i){
sum_sub[i]=nums[i]+sum_sub[i-1];
}
int res=0;//统计变量
//辅助数组
vector<long long> tmp(nums.size(),0);
//归并排序
merge_find(sum_sub,tmp,0,nums.size()-1,lower,upper,res);
return res;
}
};