开启掘金成长之旅!这是我参与「掘金日新计划 · 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];
}
}
手绘理解
下面是笔者思考过程手绘的一张图,可以帮助理解:
总结
本文是笔者由一道力扣题
引发的思考,涉及 预处理数组
,前缀和
等知识,「保持学习 主动思考 坚持输出」
,我是前端涤生
,希望这篇文章可以帮到您🍺🍺