题目
给定一个整数数组 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]
)
示例 1:
- 输入:
["NumArray", "sumRange", "sumRange", "sumRange"]
[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
- 输出:
[null, 1, -1, -3]
- 解释:
NumArray 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))
方法一:前缀和
思路及解法
最朴素的想法是存储数组 的值,每次调用 时,通过循环的方法计算数组 从下标 到下标 范围内的元素和,需要计算 个元素的和。由于每次检索的时间和检索的下标范围有关,因此检索的时间复杂度较高,如果检索次数较多,则会超出时间限制。
由于会进行多次检索,即多次调用 ,因此为了降低检索的总时间,应该降低 的时间复杂度,最理想的情况是时间复杂度 。为了将检索的时间复杂度降到 ,需要在初始化的时候进行预处理。
注意到当 时, 可以写成如下形式:
由此可知,要计算 ,则需要计算数组 在下标 和下标 的前缀和,然后计算两个前缀和的差。
如果可以在初始化的时候计算出数组 在每个下标处的前缀和,即可满足每次调用 的时间复杂度都是 。
具体实现方面,假设数组 的长度为 ,创建长度为 的前缀和数组 ,对于 都有 ,则当 时, 表示数组 从下标 到下标 的前缀和。
将前缀和数组 的长度设为 的目的是为了方便计算 ,不需要对 的情况特殊处理。此时有:
代码
class NumArray {
var sums: [Int]
init(_ nums: [Int]) {
sums = Array.init(repeating: 0, count: nums.count + 1)
for i in 0..<nums.count {
sums[i + 1] = sums[i] + nums[i]
}
}
func sumRange(_ left: Int, _ right: Int) -> Int {
return sums[right + 1] - sums[left]
}
}
复杂度分析
时间复杂度:初始化 ,每次检索 ,其中 是数组 的长度。
初始化需要遍历数组 计算前缀和,时间复杂度是 。
每次检索只需要得到两个下标处的前缀和,然后计算差值,时间复杂度是 。空间复杂度:,其中 是数组 的长度。需要创建一个长度为 的前缀和数组。