不考虑时间复杂度,采取暴力法,两个for循环当然是可以求出二维矩阵的数字之和,时间复杂度是O(mn),mn分别是矩阵的行数和列数,但题目提到要被反复调用多次,所以应优化求和过程。
同样是利用累加数组数字求子数组之和,不难想到,计算左上角坐标(r_1,c_2)、右下角坐标(r_2,c_2)的子矩阵之和可以用:左上角坐标(0,0)、右下角坐标(r_2,c_2)的子矩阵之和减去左上角坐标(0,0)、右下角坐标(r_1-1,c_2)的子矩阵之和减去左上角坐标(0,0)、右下角坐标(r_2,c_1-1)的子矩阵之和再加上左上角坐标(0,0)、右下角坐标(r_1-1,c_1-1)的子矩阵之和,四个矩阵计算。
因此,可以先用一个辅助矩阵sums记录左上角坐标为(0,0)到每个右下角坐标的子矩阵之和。而计算sums[i][j]可以分成两部分计算,第一部分是左上角坐标(0,0),右下角坐标(i-1,j)的子矩阵之和,即sums[i-1][j],第二部分是输入矩阵第i行中第0列到第j列的数字之和。
不过为了简化区域和的计算,会增加一行和一列,避免在计算部分和时要检查边界,使得代码更加简洁和高效,也就是初始化 sums 数组时,维度要比输入矩阵大1。具体来说,在sums数组中,sums[i][j] 表示从输入矩阵(0,0)到(i-1,j-1)的所有元素的和。因为原先sums[row2][col2] - sums[row1-1][col2] - sums[row2][col1-1] + sums[row1-1][col1-1]中row1-1和cow-1可能为负数,而这样在计算时,可以通过sums[i+1][j+1]表示从(0,0) 到(i,j)的部分和,即sums[row2 + 1][col2 + 1] - sums[row1][col2 + 1] - sums[row2 + 1][col1] + sums[row1][col1]。
public class NumMatrix {
private int[][] sums; // 存储从输入矩阵 (0,0) 到 (i,j) 的元素和
// 构造函数,用于初始化 sums 数组
public NumMatrix(int[][] matrix) {
if (matrix.length == 0 || matrix[0].length == 0) {
return; // 处理空矩阵的情况
}
// 初始化 sums 数组,维度比原始矩阵大1
sums = new int[matrix.length + 1][matrix[0].length + 1];
// 使用动态规划计算部分和
for (int i = 0; i < matrix.length; i++) {
int rowSum = 0;
for (int j = 0; j < matrix[0].length; j++) {
rowSum += matrix[i][j];
sums[i + 1][j + 1] = sums[i][j + 1] + rowSum; // 存储部分和
}
}
}
// 计算给定区域 (row1, col1) 到 (row2, col2) 的元素和
public int sumRegion(int row1, int col1, int row2, int col2) {
// 利用预先计算好的 sums 数组计算区域和
return sums[row2 + 1][col2 + 1] - sums[row1][col2 + 1] - sums[row2 + 1][col1] + sums[row1][col1];
}
}
该算法用sumRegion求子矩阵的数字之和,每次调用时间复杂度为O(1),而构造函数预处理的时间复杂度是O(mn),空间复杂度是O(mn),mn分别是矩阵的行数和列数。