面试题-不可变数组快速范围求和

458 阅读1分钟

题目

给定一个整数数组 nums,计算出从第 i 个元素到第 j 个元素的和 ( i ≤ j ),包括 nums[i] 和 nums[j]。

例子:

const arr = Object.freeze([1, 2, 3, 4, 5, 6])
sumRange(0, 2) // 6
sumRange(2, 5) // 18
sumRange(0, 5) // 21

注意:

1、数组的值不会改变 2、sumRange 可能会被使用很多次,求不同范围的值 3、数组可能规模很大(比如超过 10000 个数),注意运行时间

简单实现

function sumRange(i, j){
  let sum = 0;
  for(; i <= j; i++){
    sum += arr[i];
  }
  return sum;
}

考虑到注意事项中的2、3,简单实现的代码是非常慢的,在数组非常大时,可能会导致程序超时。

查表方法

二维表可以将每一对 i, j 完全映射一个值,空间复杂度为 O( n2),但是在数组非常大时,二维映射表就可能内存溢出了。

//    0  1  2  3  4   5
// 0  1  3  6  10 15  21
// 1     2  5  9  14  20
// 2        3  7  12  18
// 3           4  9   15
// 4              5   11
// 5                  6

由于数组在初始化后值不会再改变,这里可以发现一个规律。

sumRange(i, j) === sumRange(0, j) - sumRange(0, i - 1);

下面是代码实现

function sumConstructor(arr) {
  this.sums = [];
  this.arr = arr;
}

sumConstructor.prototype.sumRange = function (i, j) {
  const { arr, sums } = this;
  const lastIndex = arr.length - 1;
  if (i < 0) i = 0;
  if (j < 0) j = 0;
  if (i > lastIndex) i = lastIndex;
  if (j > lastIndex) j = lastIndex;
  if (!sums.length) {
    this.sums.push(0);
    let sum = 0;
    for (let i = 0; i < arr.length; i++) {
      sum += arr[i];
      this.sums.push(sum);
    }
  }
  return this.sums[j + 1] - this.sums[i];
}

const arr = Object.freeze([1, 2, 3, 4, 5, 6]);

const sum = new sumConstructor(arr);

console.log(sum.sumRange(0, 2))
console.log(sum.sumRange(0, 6))