算法初探LeetCode-区间和的个数

115 阅读2分钟

LeetCode327. 区间和的个数

给你一个整数数组 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<=1051 <= nums.length <= 10^5
  • 231<=nums[i]<=2311-2^{31} <= nums[i] <= 2^{31} - 1
  • 105<=lower<=upper<=105-10^5 <= lower <= upper <= 10^5
  • 题目数据保证答案是一个 32 位 的整数

思路分析

解决这题首先要明白一个事实,就是i < j其实可以理解为j的相对位置在i之后,或者是说不一定需要严格的要求保持nums[i]和nums[j]的位置。 就好比要统计两个数组a和b中,满足b[j] > a[i] 的总对数(a = [1,2,3], b=[1,7] 总对数是<1,7><2,7><3,7>这三对),b中元素的位置可以任意改变, 这并不会影响最终的统计结果。理解这一点后就很好理解归并排序中将两组合并前的排序过程,排序会改变元素的位置,但是前后两组的元素相对顺序是不变的,前一组元素一定在先

考虑这样一个问题,给定升序数组 a=[1,4,5] 和 b=[2,3,5,6],统计出满足 a[i] > 2 * b[j] 的总个数。 可以这样考虑,初始设定让 left 指向a数组头部, right 指向b数组头部,判断 a[left] 和 2*b[right] 的大小,假如 a[left] 比较小,那说明对于b中任意元素,都不会有符合条件的情况出现,因为此时right指向b中最小元素,剩下的元素大于b[right],更不可能比a[left]小。left右移就。

给你一个区间 [m,n],再给你一个数字x,那么这个区间加这个数字是不是就是新的区间[m+x, n+x]呢。 现在有一个升序数组 a 和升序数组 b, 寻找满足 b[j] - a[i] 属于某个区间的元素,可以考虑将a[i]暂时固定,假设为a[0] ,那么问题变为找到b[j] - a[0] 属于区间[m,n], 就是去找b数组中在[m+a[0], n+a[0]]的元素个数。然后去考虑a[1],a[2]...

算法代码

int m;
int[] tr = new int[100010 * 3];
int lowbit(int x) {
    return x & -x;
}
void add(int x, int v) {
    for (int i = x; i <= m; i += lowbit(i)) tr[i] += v;
}
int query(int x) {
    int ans = 0;
    for (int i = x; i > 0; i -= lowbit(i)) ans += tr[i];
    return ans;
}
public int countRangeSum(int[] nums, int lower, int upper) {
    Set < Long > set = new HashSet < > ();
    long s = 0;
    set.add(s);
    for (int i: nums) {
        s += i;
        set.add(s);
        set.add(s - lower);
        set.add(s - upper);
    }
    List < Long > list = new ArrayList < > (set);
    Collections.sort(list);
    Map < Long, Integer > map = new HashMap < > ();
    for (long x: list) map.put(x, ++m);
    s = 0;
    int ans = 0;
    add(map.get(s), 1);
    for (int i: nums) {
        s += i;
        int a = map.get(s - lower), b = map.get(s - upper) - 1;
        ans += query(a) - query(b);
        add(map.get(s), 1);
    }
    return ans;
}

结果详情

Snipaste_2023-06-30_16-08-55.png

算法复杂度

  • 空间复杂度:O(n)O(n)
  • 时间复杂度:O(nlogn)O(nlogn)

掘金(JUEJIN)一起进步,一起成长!