二维区域和检索 - 矩阵不可变
难度:
标签:
题目描述
给定一个二维矩阵 matrix
,以下类型的多个请求:
- 计算其子矩形范围内元素的总和,该子矩阵的 左上角 为
(row1, col1)
,右下角 为(row2, col2)
。
实现 NumMatrix
类:
NumMatrix(int[][] matrix)
给定整数矩阵matrix
进行初始化int sumRegion(int row1, int col1, int row2, int col2)
返回 左上角(row1, col1)
、右下角(row2, col2)
所描述的子矩阵的元素 总和 。
示例 1:
输入: ["NumMatrix","sumRegion","sumRegion","sumRegion"] [[[[3,0,1,4,2],[5,6,3,2,1],[1,2,0,1,5],[4,1,0,1,7],[1,0,3,0,5]]],[2,1,4,3],[1,1,2,2],[1,2,2,4]] 输出: [null, 8, 11, 12] 解释: NumMatrix numMatrix = new NumMatrix([[3,0,1,4,2],[5,6,3,2,1],[1,2,0,1,5],[4,1,0,1,7],[1,0,3,0,5]]); numMatrix.sumRegion(2, 1, 4, 3); // return 8 (红色矩形框的元素总和) numMatrix.sumRegion(1, 1, 2, 2); // return 11 (绿色矩形框的元素总和) numMatrix.sumRegion(1, 2, 2, 4); // return 12 (蓝色矩形框的元素总和)
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 200
-105 <= matrix[i][j] <= 105
0 <= row1 <= row2 < m
0 <= col1 <= col2 < n
- 最多调用
104
次sumRegion
方法
代码结果
运行时间: 333 ms, 内存: 27.9 MB
/*
* 思路:使用Java Stream无法直接处理二维数组的前缀和问题,因为Stream API主要处理单一维度的数据流。
* 但我们仍然可以使用Stream API来初始化前缀和数组。
*/
import java.util.stream.IntStream;
public class NumMatrix {
private int[][] prefixSums;
public NumMatrix(int[][] matrix) {
int m = matrix.length;
int n = matrix[0].length;
prefixSums = new int[m + 1][n + 1];
IntStream.range(1, m + 1).forEach(i -> {
IntStream.range(1, n + 1).forEach(j -> {
prefixSums[i][j] = matrix[i - 1][j - 1] + prefixSums[i - 1][j] + prefixSums[i][j - 1] - prefixSums[i - 1][j - 1];
});
});
}
public int sumRegion(int row1, int col1, int row2, int col2) {
return prefixSums[row2 + 1][col2 + 1] - prefixSums[row2 + 1][col1] - prefixSums[row1][col2 + 1] + prefixSums[row1][col1];
}
}
解释
方法:
这个题解使用了二维前缀和的思路。具体来说,通过预处理计算矩阵的二维前缀和,然后利用前缀和数组快速计算任意子矩形的元素和。在预处理阶段,先计算第一行的前缀和,然后逐行计算每一行的前缀和,其中每个位置的前缀和等于当前行到当前位置的元素和加上上一行相同列的前缀和。在查询阶段,对于任意的子矩形,可以利用预处理得到的前缀和数组,使用四个前缀和的加减运算得到子矩形的元素和,需要注意处理边界情况。
时间复杂度:
O(mn + q)
空间复杂度:
O(mn)
代码细节讲解
🦆
在计算二维前缀和时,你是如何处理矩阵的第一行和第一列的?
▷🦆
为什么在计算前缀和时要加上上一行相同列的前缀和?这个操作的数学逻辑是什么?
▷🦆
在查询函数`sumRegion`中,如何理解使用四个前缀和来计算子矩阵的元素总和的方法?具体的运算逻辑是怎样的?
▷🦆
当查询子矩阵的边界超出原始矩阵的范围时,如何处理这种情况?题解中有没有提及相关的边界检查?
▷相关问题
区域和检索 - 数组不可变
给定一个整数数组 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))
提示:
1 <= nums.length <= 104
-105 <= nums[i] <= 105
0 <= i <= j < nums.length
- 最多调用
104
次sumRange
方法