题目描述(中等难度)
给定矩阵的左上角坐标和右下角坐标,返回矩阵内的数字累计的和。
解法一
和 上一道题 其实差不多,上一个题是一维空间的累计,这个是二维,没做过上一题,可以先看一下,这里用同样的思路了。
如果我们只看矩阵的某一行,那其实就变成上一题了。所以我们可以提前把每一行各自的累和求出来,然后求整个矩阵的累和的时候,一行一行求即可。
/**
* @param {number[][]} matrix
*/
var NumMatrix = function (matrix) {
this.rowsAccumulate = [];
let rows = matrix.length;
if(rows === 0){
return;
}
let cols = matrix[0].length;
for (let i = 0; i < rows; i++) {
let row = [0];
let sum = 0;
for (let j = 0; j < cols; j++) {
sum += matrix[i][j];
row.push(sum);
}
this.rowsAccumulate.push(row);
}
};
/**
* @param {number} row1
* @param {number} col1
* @param {number} row2
* @param {number} col2
* @return {number}
*/
NumMatrix.prototype.sumRegion = function (row1, col1, row2, col2) {
let sum = 0;
for (let i = row1; i <= row2; i++) {
sum = sum + this.rowsAccumulate[i][col2 + 1] - this.rowsAccumulate[i][col1];
}
return sum;
};
/**
* Your NumMatrix object will be instantiated and called as such:
* var obj = new NumMatrix(matrix)
* var param_1 = obj.sumRegion(row1,col1,row2,col2)
*/
解法二
当然,我们也可以忘记上一道题的解法,重新分析,但思想还是上一题的思想。
我们可以用 matrixAccumulate[i][j]
来保存从 (0, 0)
到 (i - 1, j - 1)
矩阵内所有数累计的和。
matrixAccumulate[0][*]
和 matrixAccumulate[*][0]
都置为 0
,这样做的好处就是为了统一处理边界的情况,看完下边的解法,可以回过头来思考。
然后和上一道题一样,对于 (row1, col1)
和 (row2, col2)
这两个点组成的矩阵内数字的累计和可以表示为下边的式子。
this.matrixAccumulate[row2 + 1][col2 + 1] -
this.matrixAccumulate[row1][col2 + 1] -
this.matrixAccumulate[row2 + 1][col1] +
this.matrixAccumulate[row1][col1]
至于为什么这样,可以结合下边的图。
我们要求的是橙色部分的矩阵。只需要用 (0, 0)
到 (row2, col2)
的矩阵,减去 (0, 0)
到 (row1, col2)
的矩阵,再减去 (0, 0)
到 (row2, col1)
的矩阵,最后加上 (0, 0)
到 (row1, col1)
的矩阵。因为 (0, 0)
到 (row1, col1)
的矩阵多减了一次。
然后可以看看坐标的分布,就可以得出上边的式子了。
之所以出现,row2 + 1
、co2 + 1
这种坐标,是因为我们的 matrixAccumulate[i][j]
来保存从 (0, 0)
到 (i - 1, j - 1)
,有一个减 1
的操作。
至于 matrixAccumulate
怎么求,我们可以使用上边类似的方法,通过矩阵的加减实现。
O
到 A
的累加,就等于 A
位置的值加上 O
的 C
的累加,加上 O
的 B
的累加,减去 O
到 D
的累加。代码的话,就是下边的样子。
this.matrixAccumulate[i][j] =
matrix[i-1][j-1] +
this.matrixAccumulate[i - 1][j] +
this.matrixAccumulate[i][j - 1] -
this.matrixAccumulate[i - 1][j - 1];
}
总代码就是下边的了。
/**
* @param {number[][]} matrix
*/
var NumMatrix = function (matrix) {
this.matrixAccumulate = [];
let rows = matrix.length;
if (rows === 0) {
return;
}
let cols = matrix[0].length;
for (let i = 0; i <= rows; i++) {
let row = [];
for (let j = 0; j <= cols; j++) {
row.push(0);
}
this.matrixAccumulate.push(row);
}
for (let i = 1; i <= rows; i++) {
for (let j = 1; j <= cols; j++) {
this.matrixAccumulate[i][j] =
matrix[i-1][j-1] +
this.matrixAccumulate[i - 1][j] +
this.matrixAccumulate[i][j - 1] -
this.matrixAccumulate[i - 1][j - 1];
}
}
};
/**
* @param {number} row1
* @param {number} col1
* @param {number} row2
* @param {number} col2
* @return {number}
*/
NumMatrix.prototype.sumRegion = function (row1, col1, row2, col2) {
return (
this.matrixAccumulate[row2 + 1][col2 + 1] -
this.matrixAccumulate[row1][col2 + 1] -
this.matrixAccumulate[row2 + 1][col1] +
this.matrixAccumulate[row1][col1]
);
};
/**
* Your NumMatrix object will be instantiated and called as such:
* var obj = new NumMatrix(matrix)
* var param_1 = obj.sumRegion(row1,col1,row2,col2)
*/
再分享 StefanPochmann 大神的一个思路,上边我们用 matrixAccumulate[i][j]
来保存从 (0, 0)
到 (i - 1, j - 1)
矩阵内所有数累计的和,多了减一。虽然这种思路经常用到,就像字符串截取函数一样,一般都是包括左端点,不包括右端点,但看起来比较绕。
我们可以用 matrixAccumulate[i][j]
来保存从 (0, 0)
到 (i, j)
矩阵内所有数累计的和,这样的话,为了避免单独判断边界情况的麻烦,我们可以封装一个函数,对于下标小于 0
的边界情况直接返回 0
,参考下边的代码。
/**
* @param {number[][]} matrix
*/
var NumMatrix = function (matrix) {
this.matrixAccumulate = matrix;
let rows = matrix.length;
if (rows === 0) {
return;
}
let cols = matrix[0].length;
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
this.matrixAccumulate[i][j] +=
this.f(i - 1, j) + this.f(i, j - 1) - this.f(i - 1, j - 1);
}
}
};
/**
* @param {number} row1
* @param {number} col1
* @param {number} row2
* @param {number} col2
* @return {number}
*/
NumMatrix.prototype.sumRegion = function (row1, col1, row2, col2) {
return (
this.f(row2, col2) -
this.f(row1 - 1, col2) -
this.f(row2, col1 - 1) +
this.f(row1 - 1, col1 - 1)
);
};
NumMatrix.prototype.f = function (i, j) {
return i >= 0 && j >= 0 ? this.matrixAccumulate[i][j] : 0;
};
/**
* Your NumMatrix object will be instantiated and called as such:
* var obj = new NumMatrix(matrix)
* var param_1 = obj.sumRegion(row1,col1,row2,col2)
*/
总
比较简单的一道题,基本上还是上一题的思路,想起来小学求矩形面积了,哈哈。解法二的话两种技巧都是处理边界情况的方法,将边界的逻辑和其他部分的逻辑统一了起来,前一种扩充 0
的技巧比较常用。