手撕LeetCode求区间的个数

/*
题目描述:
给定一个整数数组,返回区间和在[lower,upper]之间的个数,包含lower和upper,区间和S(i,j)
表示在nums中,位置从i到j的元素之和,包含i和j(i=<j)。

注意:
最直观的时间复杂度是O(n^2),请在此基础上优化你的算法。

示例:
Input:nums=[-2,5,1],lower=-2,upper=2
Output:3
其中3个区间:[0,0],[2,2],[0,2]它们表示的和分别为:-2,-1,2

解题思路:
归并排序
(1)求区间和,一般先构建前缀和数组进行数据预处理,可以O(1)得到任意区间和;
(2)查找元素,一般先进行排序,利用单调性可以快速的缩减查找范围,减少时间复杂度;
(3)归并排序包含了两个过程:从上往下的分解:把当前区间一分为二,直至分解为若干个
长度为1的子数组。从下往上的合并:两个有序的子区域两两向上合并;
(4)对于本题,归并过程中,右区间中元素分别和左区间中元素的差即原数组的各区间和,利用
有序可以减少重复的比较操作;
*/

C++语言实现:

#include <iostream>
#include <vector>

using namespace std;

class Solution{

public:
    Solution(vector<long long>& _nums,const int& _lower,const int& _upper):nums(_nums),lower(_lower),upper(_upper){}
    int coutRangeSum(){
        if(nums.empty())
           return 0;
        vector<long long> presum;//前缀和数组,[0,i]区间和
        long long pre=0;
        for(auto& num:nums){
            pre+=num;
            presum.emplace_back(pre);
        }
        int result=0;
        vector<long long> temp(nums.size());//用于合并两个有序数组的临时数组
        mergesort(presum,lower,upper,temp,0,nums.size()-1,result);
        
        return result;
    }

private:
    void mergesort(vector<long long>& presum,int& low,int& up,vector<long long>& temp,int left,int right,int& result){
        if(left==right){
            if(presum[left]>=low&&presum[left]<=up)
               result++;
            return;
        }
        int mid=left+(right-left)/2;
        mergesort(presum,low,up,temp,left,mid,result);//使得[left,mid]有序
        mergesort(presum,low,up,temp,mid+1,right,result);//使得[mid+1,right]有序

        //合并之前先进行统计
        int i=left;//i指向左区间
        int j_left=mid+1;
        int j_right=mid+1;//j_left、j_right指向右区间,i<j,相减得到区间和
        //i固定时,j越大差越大;j固定时,i越大差越小
        while(i<mid+1){
            while(j_left<=right&&presum[j_left]-presum[i]<low)//找到下限位置
                j_left++;
            j_right=j_left;
            while(j_right<=right&&presum[j_right]-presum[i]<=up){//找到上限位置
                j_right++;
                result++; //找到一对
            }
            i++;
        }

        //合并
        i=left;
        int j=mid+1,t=0;
        while(i<=mid&&j<=right){
            if(presum[i]<=presum[j])
                temp[t++]=presum[i++];
            else
                temp[t++]=presum[j++];
        }

        while(i<=mid)
            temp[t++]=presum[i++];
    
        
        while(j<=right)
            temp[t++]=presum[j++];

        t=0,i=left;
        while(i<=right)
            presum[i++]=temp[t++];
    }
private:
    vector<long long> nums;
    int lower,upper;
};

int main(int argc,char* argv[]){
    vector<long long> nums={-2,5,-6};
    Solution solu(nums,-2,2);
    cout<<solu.coutRangeSum()<<endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路上的追梦人

您的鼓励就是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值