327. 区间和的个数

187 阅读1分钟

题目描述

leetcode-cn.com/problems/co…

分析

区间问题要想到前缀和

本题还需要用归并思想去加速统计过程

算法

前缀和,归并

过程

计算前缀和

用归并排序代码操作前缀和数组

拿到左右两个排序后的区间,进行统计

区间和在一定范围内,实际上对于我们的前缀和数组来说,就需要不断地确认前缀和数组的两个 index

可以遍历右区间,计算一个范围 a, b,再去找左区间中符合范围的最大值,这个最大值只能有一个,因为左区间是递增的

代码

/**
 * @param {number[]} nums
 * @param {number} lower
 * @param {number} upper
 * @return {number}
 */
var countRangeSum = function (nums, lower, upper) {
  const sum = [0]
  for (let i = 0; i < nums.length; i++) {
    sum[i + 1] = nums[i] + sum[i]
  }

  return mergeSort(sum, 0, sum.length - 1, lower, upper)
}

function mergeSort(sum, l, r, lower, upper) {
  if (l >= r) return 0
  const mid = (l + r) >> 1,
    temp = []
  let p1 = l,
    p2 = mid + 1,
    ans = 0,
    k = 0

  ans += mergeSort(sum, p1, mid, lower, upper)
  ans += mergeSort(sum, p2, r, lower, upper)
  ans += countTwoParts(sum, p1, mid, p2, r, lower, upper)

  while (p1 <= mid || p2 <= r) {
    if (p2 > r || (p1 <= mid && sum[p1] <= sum[p2])) {
      temp[k++] = sum[p1++]
    } else {
      temp[k++] = sum[p2++]
    }
  }
  for (let i = l; i <= r; i++) {
    sum[i] = temp[i - l]
  }

  return ans
}

function countTwoParts(sum, l1, r1, l2, r2, lower, upper) {
  let ans = 0,
    k1 = l1,
    k2 = l1

  // 找一个 sum[j]
  for (let j = l2; j <= r2; j++) {
    let a = sum[j] - upper
    let b = sum[j] - lower

    // 求第一个位置 >= a,第一个位置 > b
    while (k1 <= r1 && sum[k1] < a) k1++
    // 此时 k1 指向的是第一个 >= a 的位置

    while (k2 <= r1 && sum[k2] <= b) k2++
    // 此时 k2 指向的是第一个 > b 的位置

    ans += k2 - k1
  }

  return ans
}