[路飞]_leetcode-327-区间和的个数

254 阅读1分钟

题目描述

[题目地址]

给你一个整数数组 nums 以及两个整数 lower 和 upper 。求数组中,值位于范围 [lower, upper] (包含 lower 和 upper)之内的 区间和的个数 。

区间和 S(i, j) 表示在 nums 中,位置从 i 到 j 的元素之和,包含 i 和 j (i ≤ j)。

示例 1:

输入: nums = [-2,5,-1], lower = -2, upper = 2
输出: 3
解释: 存在三个区间:[0,0][2,2][0,2] ,对应的区间和分别是:-2 、-1 、2 。

示例 2:

输入: nums = [0], lower = 0, upper = 0
输出: 1

提示:

  • 1 <= nums.length <= 105
  • -231 <= nums[i] <= 231 - 1
  • -105 <= lower <= upper <= 105
  • 题目数据保证答案是一个 32 位 的整数

前缀和数组

所谓前缀和数组,就是数组中的每一项,等于原数组中当前位置及之前元素的和值。
例如原数组为 [1,2,3,4], 前缀和数组为 [0,1,3,6,10]。 要注意的是前缀和数组中默认第一位是 0,所以前缀和数组的长度会比原数组长度 +1
为什么要有一个前置 0呢?

  1. 方便计算前缀和数组的第一项的值。因为我们求前缀和数组中当前项值的时候,只需要将前一项的值加上原数组前位置的元素值即可,那么为了原数组第一个元素计算的时候有前一项值,所以需要一个前置 0
  2. 方便计算区间和值。观察前缀和数组可以发现,后面的值减去前面的值,刚好等于原数组中该区间中的元素和值,即区间和值。那每一项减去最前面的 0,其实就是从原数组下标 0到当前位置的区间和值(这里要注意的是,因为有一个前置 0,所以前缀和数组中的下标等于原数组中的下标 +1)。

解题思路

涉及到求区间和的问题,我们可以尝试使用前缀和数组来解题。
这里我们首先求得原数组的前缀和数组,拿到前缀和数组有什么用呢?我们接着往下看。

接下来,我们对前缀和数组进行 归并排序
假设此时我们来到了归并排序的回溯过程,此时左区间有序且右区间有序。并且左区间的值的下标都小于右区间的值的下标。
又因为前缀和数组中,后面的值减去前面的值等于原数组中该区间的元素和值,也就是区间和值。所以此时如果右区间的某个元素减去左区间的某个元素,差值刚好在给定的 lower~upper 之间,则说明找到了一个符合要求的区间和值。\

动画演示

leetcode-327-区间和的个数.gif

代码实现

var countRangeSum = function(nums, lower, upper) {
    let s = 0;
    const sum = [0];
    for(const v of nums){
        s += v;
        sum.push(s);
    }
    return countRangeSumRecursive(sum,lower,upper,0,sum.length -1)
};

var countRangeSumRecursive = function(sum,lower,upper,left,right){
    if(left === right){
        return 0;
    }else{
        let mid = Math.floor((left + right)/2);
        let n1 = countRangeSumRecursive(sum,lower,upper,left,mid);
        let n2 = countRangeSumRecursive(sum,lower,upper,mid+1,right);
        // 左右区间数量
        let ret = n1 + n2;
        // 首先统计一下下标的对数
        let i = left;
        let l = mid + 1;
        let r = mid + 1;
        while(i <= mid){
            while(l <= right && sum[l] - sum[i] < lower) l++;
            while(r <= right && sum[r] - sum[i] <= upper) r++;
            ret += (r-l);
            i++
        }
         // 合并两个排序数组
        const sorted = new Array(right - left + 1);
        let p1 = left, p2 = mid + 1;
        let p = 0 ;
        while(p1 <= mid || p2 <= right){
            if(p1 > mid){
                sorted[p++] = sum[p2++]
            }else if( p2 > right){
                sorted[p++] = sum[p1++]
            }else{
                if(sum[p1] < sum[p2]){
                    sorted[p++] = sum[p1++]
                }else{
                    sorted[p++] = sum[p2++]
                }
            }
        }
        for(let i = 0; i < sorted.length; i++){
            sum[left+i] = sorted[i];
        }
        return ret;
        }
   
}

至此我们就完成了leetcode-327-区间和的个数

欢迎讨论建议