LeetCode-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。
来源:力扣(LeetCode)

题目分析

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

代码示例

class Solution {
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        int size = nums.size();
        if( size == 0 ) 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(size);//用于合并两个有序数组的临时数组
        mergesort(presum,lower,upper,temp,0,size-1,result);
        return result;
    }
    void mergesort(vector<long long>& presum, int lower, int upper,vector<long long >& temp,int left,int right,int &result)
    {
        if( left == right)//分到只剩一个元素
        {
            if( presum[left] >= lower && presum[left] <= upper)
            {
                result++;
            }
            return;
        }
        int mid = left+(right-left)/2;
        mergesort(presum,lower,upper,temp,left,mid,result);//使 [left,mid] 有序
        mergesort(presum,lower,upper,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,相减得到区间和
        while( i < mid+1 )// i 固定时,j 越大差越大;j  固定时,i 越大差越小
        {
            while(j_left <= right && presum[j_left] - presum[i] < lower )//找到下限位置
            {
                j_left++;
            }
            j_right = j_left;
            while( j_right <= right && presum[j_right] - presum[i] <= upper) //找到上限位置
            {
                j_right++;
                result++;//找到一对
            }
            i++;   
        }
        //合并
        i = left;
        int j = mid+1;
        int 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++];
        }
    }
};

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值