前缀和实现题目:区域和检索 - 数组不可变
文章目录
- 题目
- 标题和出处
- 难度
- 题目描述
- 要求
- 示例
- 数据范围
- 解法
- 思路和算法
- 代码
- 复杂度分析
题目
标题和出处
标题:区域和检索 - 数组不可变
出处:303. 区域和检索 - 数组不可变
难度
3 级
题目描述
要求
给定一个整数数组 nums \texttt{nums} nums,处理以下类型的多个查询:
- 计算下标 left \texttt{left} left 和 right \texttt{right} right 之间的 nums \texttt{nums} nums 元素的和(包含 left \texttt{left} left 和 right \texttt{right} right),其中 left ≤ right \texttt{left} \le \texttt{right} left≤right。
实现 NumArray \texttt{NumArray} NumArray 类:
- NumArray(int[] nums) \texttt{NumArray(int[] nums)} NumArray(int[] nums) 使用数组 nums \texttt{nums} nums 初始化对象。
- int sumRange(int left, int right) \texttt{int sumRange(int left, int right)} int sumRange(int left, int right) 返回数组 nums \texttt{nums} nums 中的下标 left \texttt{left} left 和 right \texttt{right} right 之间的元素的和,包含 left \texttt{left} left 和 right \texttt{right} right(即 nums[left] + nums[left + 1] + ... + nums[right] \texttt{nums[left] + nums[left + 1] + ... + nums[right]} nums[left] + nums[left + 1] + ... + nums[right])。
示例
示例 1:
输入:
["NumArray", "sumRange", "sumRange", "sumRange"] \texttt{["NumArray", "sumRange", "sumRange", "sumRange"]} ["NumArray", "sumRange", "sumRange", "sumRange"]
[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]] \texttt{[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]} [[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
输出:
[null, 1, -1, -3] \texttt{[null, 1, -1, -3]} [null, 1, -1, -3]
解释:
NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]); \texttt{NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);} NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);
numArray.sumRange(0, 2); \texttt{numArray.sumRange(0, 2);} numArray.sumRange(0, 2); // 返回 (-2) + 0 + 3 = 1 \texttt{(-2) + 0 + 3 = 1} (-2) + 0 + 3 = 1
numArray.sumRange(2, 5); \texttt{numArray.sumRange(2, 5);} numArray.sumRange(2, 5); // 返回 3 + (-5) + 2 + (-1) = -1 \texttt{3 + (-5) + 2 + (-1) = -1} 3 + (-5) + 2 + (-1) = -1
numArray.sumRange(0, 5); \texttt{numArray.sumRange(0, 5);} numArray.sumRange(0, 5); // 返回 (-2) + 0 + 3 + (-5) + 2 + (-1) = -3 \texttt{(-2) + 0 + 3 + (-5) + 2 + (-1) = -3} (-2) + 0 + 3 + (-5) + 2 + (-1) = -3
数据范围
- 1 ≤ nums.length ≤ 10 4 \texttt{1} \le \texttt{nums.length} \le \texttt{10}^\texttt{4} 1≤nums.length≤104
- -10 5 ≤ nums[i] ≤ 10 5 \texttt{-10}^\texttt{5} \le \texttt{nums[i]} \le \texttt{10}^\texttt{5} -105≤nums[i]≤105
- 0 ≤ left ≤ right < nums.length \texttt{0} \le \texttt{left} \le \texttt{right} < \texttt{nums.length} 0≤left≤right<nums.length
- 最多调用 10 4 \texttt{10}^\texttt{4} 104 次 sumRange \texttt{sumRange} sumRange
解法
思路和算法
这道题是一维前缀和的实现与应用。计算数组 nums \textit{nums} nums 中的任意子数组的元素和,方法是首先计算数组 nums \textit{nums} nums 的前缀和数组,然后根据前缀和数组在 O ( 1 ) O(1) O(1) 的时间内计算任意子数组的元素和。
用 n n n 表示数组 nums \textit{nums} nums 的长度,创建长度为 n + 1 n + 1 n+1 的前缀和数组 prefixSums \textit{prefixSums} prefixSums,对于 0 ≤ i ≤ n 0 \le i \le n 0≤i≤n, prefixSums [ i ] \textit{prefixSums}[i] prefixSums[i] 表示 nums \textit{nums} nums 的长度为 i i i 的前缀和,即下标范围 [ 0 , i − 1 ] [0, i - 1] [0,i−1] 中的 i i i 个元素之和:
prefixSums [ i ] = ∑ k = 0 i − 1 nums [ k ] \textit{prefixSums}[i] = \sum\limits_{k = 0}^{i - 1} \textit{nums}[k] prefixSums[i]=k=0∑i−1nums[k]
特别地, prefixSums [ 0 ] = 0 \textit{prefixSums}[0] = 0 prefixSums[0]=0。
对于 0 ≤ i < n 0 \le i < n 0≤i<n,考虑 prefixSums [ i + 1 ] \textit{prefixSums}[i + 1] prefixSums[i+1] 与 prefixSums [ i ] \textit{prefixSums}[i] prefixSums[i] 之差:
prefixSums [ i + 1 ] − prefixSums [ i ] = ∑ k = 0 i nums [ k ] − ∑ k = 0 i − 1 nums [ k ] = ∑ k = 0 i − 1 nums [ k ] + nums [ i ] − ∑ k = 0 i − 1 nums [ k ] = nums [ i ] \begin{aligned} &\textit{prefixSums}[i + 1] - \textit{prefixSums}[i] \\ = &\sum\limits_{k = 0}^{i} \textit{nums}[k] - \sum\limits_{k = 0}^{i - 1} \textit{nums}[k] \\ = &\sum\limits_{k = 0}^{i - 1} \textit{nums}[k] + \textit{nums}[i] - \sum\limits_{k = 0}^{i - 1} \textit{nums}[k] \\ = &\textit{nums}[i] \end{aligned} ===prefixSums[i+1]−prefixSums[i]k=0∑inums[k]−k=0∑i−1nums[k]k=0∑i−1nums[k]+nums[i]−k=0∑i−1nums[k]nums[i]
因此 prefixSums [ i + 1 ] − prefixSums [ i ] = nums [ i ] \textit{prefixSums}[i + 1] - \textit{prefixSums}[i] = \textit{nums}[i] prefixSums[i+1]−prefixSums[i]=nums[i], prefixSums [ i + 1 ] = prefixSums [ i ] + nums [ i ] \textit{prefixSums}[i + 1] = \textit{prefixSums}[i] + \textit{nums}[i] prefixSums[i+1]=prefixSums[i]+nums[i]。当 prefixSums [ i ] \textit{prefixSums}[i] prefixSums[i] 的值已知时,可以使用 O ( 1 ) O(1) O(1) 的时间计算 prefixSums [ i + 1 ] \textit{prefixSums}[i + 1] prefixSums[i+1] 的值,因此可以使用 O ( n ) O(n) O(n) 的时间计算整个前缀和数组。
得到前缀和数组 prefixSums \textit{prefixSums} prefixSums 之后,对于 0 ≤ left ≤ right < n 0 \le \textit{left} \le \textit{right} < n 0≤left≤right<n,下标范围 [ left , right ] [\textit{left}, \textit{right}] [left,right] 的子数组的元素和可以通过前缀和数组得到:
sumRange ( left , right ) = prefixSums [ right + 1 ] − prefixSums [ left ] \textit{sumRange}(\textit{left}, \textit{right}) = \textit{prefixSums}[\textit{right} + 1] - \textit{prefixSums}[\textit{left}] sumRange(left,right)=prefixSums[right+1]−prefixSums[left]
代码
class NumArray {int[] prefixSums;public NumArray(int[] nums) {int n = nums.length;prefixSums = new int[n + 1];for (int i = 0; i < n; i++) {prefixSums[i + 1] = prefixSums[i] + nums[i];}}public int sumRange(int left, int right) {return prefixSums[right + 1] - prefixSums[left];}
}
复杂度分析
-
时间复杂度:构造方法的时间复杂度是 O ( n ) O(n) O(n), sumRange \textit{sumRange} sumRange 操作的时间复杂度是 O ( 1 ) O(1) O(1),其中 n n n 是数组 nums \textit{nums} nums 的长度。构造方法需要创建长度为 n + 1 n + 1 n+1 的前缀和数组并计算每个元素的前缀和,每次 sumRange \textit{sumRange} sumRange 操作计算子数组的元素和的时间是 O ( 1 ) O(1) O(1)。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。需要创建长度为 n + 1 n + 1 n+1 的前缀和数组。