开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 12 天,点击查看活动详情
背景
今天笔者刷了一道力扣题 区域和检索 - 数组不可变,略有所得,分享给掘友🍺。
另外,笔者也开了一个新的 repo,沉淀自己刷题过程中的思考,欢迎大佬围观👏👏
题目
给定一个整数数组 nums,处理以下类型的多个查询:
- 计算索引
left和right(包含left和right)之间的nums元素的 和 ,其中left <= right
实现 NumArray 类:
NumArray(int[] nums)使用数组nums初始化对象int sumRange(int i, int j)返回数组nums中索引left和right之间的元素的 总和 ,包含left和right两点(也就是nums[left] + nums[left + 1] + ... + nums[right])
示例:
class NumArray {
....
}
const numArray = new NumArray([-2, 0, 3, -5, 2, -1]);
numArray.sumRange(0, 2); // return 1 ((-2) + 0 + 3)
numArray.sumRange(2, 5); // return -1 (3 + (-5) + 2 + (-1))
numArray.sumRange(0, 5); // return -3 ((-2) + 0 + 3 + (-5) + 2 + (-1))
思路
最容易想到的是每次通过遍历累加 left...right 的 nums元素 来求结果,但是这样时间复杂度达到了 O(N),其实有更好的做法。
我们可以采用预处理数组的方式来做,根据 nums 来构建一个前缀和数组,每次查询只需要从前缀和数组 里查询 2 个点即可,这只需要 O(1) 的时间复杂度。
具体的做法
声明一个 nums 的前缀和数组 sums,sums[i] 的值等于 nums[0]+nums[1]+...+nums[i],那么:
- 当
i === 0时,sums[0] = nums[0]。 - 当
i > 0时,sum[i] = sum[i-1] + nums[i]。
我们要计算的是 nums[left] + nums[left+1] + nums[left+2] + ... + nums[right] 的和,那么:
- 当
left === 0时,就是nums[0]+nums[1]+...+nums[right],也就是sums[right]的值。 - 当
left > 0时,等同于:
(nums[0] + nums[1] + ... + nums[right]) - (nums[0] + nums[1] + ... + nums[left-1])
也就是:
sums[right] - sums[left-1]
于是,最终代码如下:
class NumArray {
private _sums: number[];
constructor(nums: number[]) {
this._sums = Array(nums.length).fill(0);
for (let i = 0, len = nums.length; i < len; i++) {
this._sums[i] = i > 0 ? (this._sums[i - 1] + nums[i]) : nums[0];
}
}
sumRange(left: number, right: number): number {
return left > 0 ? (this._sums[right] - this._sums[left - 1]) : this._sums[right];
}
}
手绘理解
下面是笔者思考过程手绘的一张图,可以帮助理解:
总结
本文是笔者由一道力扣题引发的思考,涉及 预处理数组,前缀和 等知识,「保持学习 主动思考 坚持输出」,我是前端涤生,希望这篇文章可以帮到您🍺🍺