327 区间和的个数

题目描述:
给定一个整数数组 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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值