|
| 1 | +# 题目描述(中等难度) |
| 2 | + |
| 3 | + |
| 4 | + |
| 5 | +给定矩阵的左上角坐标和右下角坐标,返回矩阵内的数字累计的和。 |
| 6 | + |
| 7 | +# 解法一 |
| 8 | + |
| 9 | +和 [上一道题](https://leetcode.wang/leetcode-303-Range-Sum-Query-Immutable.html) 其实差不多,上一个题是一维空间的累计,这个是二维,没做过上一题,可以先看一下,这里用同样的思路了。 |
| 10 | + |
| 11 | +如果我们只看矩阵的某一行,那其实就变成上一题了。所以我们可以提前把每一行各自的累和求出来,然后求整个矩阵的累和的时候,一行一行求即可。 |
| 12 | + |
| 13 | +```javascript |
| 14 | +/** |
| 15 | + * @param {number[][]} matrix |
| 16 | + */ |
| 17 | +var NumMatrix = function (matrix) { |
| 18 | + this.rowsAccumulate = []; |
| 19 | + let rows = matrix.length; |
| 20 | + if(rows === 0){ |
| 21 | + return; |
| 22 | + } |
| 23 | + let cols = matrix[0].length; |
| 24 | + for (let i = 0; i < rows; i++) { |
| 25 | + let row = [0]; |
| 26 | + let sum = 0; |
| 27 | + for (let j = 0; j < cols; j++) { |
| 28 | + sum += matrix[i][j]; |
| 29 | + row.push(sum); |
| 30 | + } |
| 31 | + this.rowsAccumulate.push(row); |
| 32 | + } |
| 33 | +}; |
| 34 | + |
| 35 | +/** |
| 36 | + * @param {number} row1 |
| 37 | + * @param {number} col1 |
| 38 | + * @param {number} row2 |
| 39 | + * @param {number} col2 |
| 40 | + * @return {number} |
| 41 | + */ |
| 42 | +NumMatrix.prototype.sumRegion = function (row1, col1, row2, col2) { |
| 43 | + let sum = 0; |
| 44 | + for (let i = row1; i <= row2; i++) { |
| 45 | + sum = sum + this.rowsAccumulate[i][col2 + 1] - this.rowsAccumulate[i][col1]; |
| 46 | + } |
| 47 | + return sum; |
| 48 | +}; |
| 49 | + |
| 50 | +/** |
| 51 | + * Your NumMatrix object will be instantiated and called as such: |
| 52 | + * var obj = new NumMatrix(matrix) |
| 53 | + * var param_1 = obj.sumRegion(row1,col1,row2,col2) |
| 54 | + */ |
| 55 | +``` |
| 56 | + |
| 57 | +# 解法二 |
| 58 | + |
| 59 | +当然,我们也可以忘记上一道题的解法,重新分析,但思想还是上一题的思想。 |
| 60 | + |
| 61 | +我们可以用 `matrixAccumulate[i][j]` 来保存从 `(0, 0)` 到 `(i - 1, j - 1)` 矩阵内所有数累计的和。 |
| 62 | + |
| 63 | +`matrixAccumulate[0][*]` 和 `matrixAccumulate[*][0]` 都置为 `0` ,这样做的好处就是为了统一处理边界的情况,看完下边的解法,可以回过头来思考。 |
| 64 | + |
| 65 | +然后和上一道题一样,对于 `(row1, col1)` 和 `(row2, col2)` 这两个点组成的矩阵内数字的累计和可以表示为下边的式子。 |
| 66 | + |
| 67 | +```javascript |
| 68 | +this.matrixAccumulate[row2 + 1][col2 + 1] - |
| 69 | + this.matrixAccumulate[row1][col2 + 1] - |
| 70 | + this.matrixAccumulate[row2 + 1][col1] + |
| 71 | + this.matrixAccumulate[row1][col1] |
| 72 | +``` |
| 73 | + |
| 74 | +至于为什么这样,可以结合下边的图。 |
| 75 | + |
| 76 | + |
| 77 | + |
| 78 | +我们要求的是橙色部分的矩阵。只需要用 `(0, 0)` 到 `(row2, col2)` 的矩阵,减去 `(0, 0)` 到 `(row1, col2)` 的矩阵,再减去 `(0, 0)` 到 `(row2, col1)` 的矩阵,最后加上 `(0, 0)` 到 `(row1, col1)` 的矩阵。因为 `(0, 0)` 到 `(row1, col1)` 的矩阵多减了一次。 |
| 79 | + |
| 80 | +然后可以看看坐标的分布,就可以得出上边的式子了。 |
| 81 | + |
| 82 | + |
| 83 | + |
| 84 | +之所以出现,`row2 + 1` 、`co2 + 1 ` 这种坐标,是因为我们的 `matrixAccumulate[i][j]` 来保存从 `(0, 0)` 到 `(i - 1, j - 1)` ,有一个减 `1 ` 的操作。 |
| 85 | + |
| 86 | +至于 `matrixAccumulate` 怎么求,我们可以使用上边类似的方法,通过矩阵的加减实现。 |
| 87 | + |
| 88 | + |
| 89 | + |
| 90 | +`O` 到 `A` 的累加,就等于 `A` 位置的值加上 `O` 的 `C` 的累加,加上 `O` 的 `B` 的累加,减去 `O` 到 `D` 的累加。代码的话,就是下边的样子。 |
| 91 | + |
| 92 | +```javascript |
| 93 | +this.matrixAccumulate[i][j] = |
| 94 | + matrix[i-1][j-1] + |
| 95 | + this.matrixAccumulate[i - 1][j] + |
| 96 | + this.matrixAccumulate[i][j - 1] - |
| 97 | + this.matrixAccumulate[i - 1][j - 1]; |
| 98 | + } |
| 99 | +``` |
| 100 | + |
| 101 | +总代码就是下边的了。 |
| 102 | + |
| 103 | +```javascript |
| 104 | +/** |
| 105 | + * @param {number[][]} matrix |
| 106 | + */ |
| 107 | +var NumMatrix = function (matrix) { |
| 108 | + this.matrixAccumulate = []; |
| 109 | + let rows = matrix.length; |
| 110 | + if (rows === 0) { |
| 111 | + return; |
| 112 | + } |
| 113 | + let cols = matrix[0].length; |
| 114 | + |
| 115 | + for (let i = 0; i <= rows; i++) { |
| 116 | + let row = []; |
| 117 | + for (let j = 0; j <= cols; j++) { |
| 118 | + row.push(0); |
| 119 | + } |
| 120 | + this.matrixAccumulate.push(row); |
| 121 | + } |
| 122 | + for (let i = 1; i <= rows; i++) { |
| 123 | + for (let j = 1; j <= cols; j++) { |
| 124 | + this.matrixAccumulate[i][j] = |
| 125 | + matrix[i-1][j-1] + |
| 126 | + this.matrixAccumulate[i - 1][j] + |
| 127 | + this.matrixAccumulate[i][j - 1] - |
| 128 | + this.matrixAccumulate[i - 1][j - 1]; |
| 129 | + } |
| 130 | + } |
| 131 | +}; |
| 132 | + |
| 133 | +/** |
| 134 | + * @param {number} row1 |
| 135 | + * @param {number} col1 |
| 136 | + * @param {number} row2 |
| 137 | + * @param {number} col2 |
| 138 | + * @return {number} |
| 139 | + */ |
| 140 | +NumMatrix.prototype.sumRegion = function (row1, col1, row2, col2) { |
| 141 | + return ( |
| 142 | + this.matrixAccumulate[row2 + 1][col2 + 1] - |
| 143 | + this.matrixAccumulate[row1][col2 + 1] - |
| 144 | + this.matrixAccumulate[row2 + 1][col1] + |
| 145 | + this.matrixAccumulate[row1][col1] |
| 146 | + ); |
| 147 | +}; |
| 148 | + |
| 149 | +/** |
| 150 | + * Your NumMatrix object will be instantiated and called as such: |
| 151 | + * var obj = new NumMatrix(matrix) |
| 152 | + * var param_1 = obj.sumRegion(row1,col1,row2,col2) |
| 153 | + */ |
| 154 | +``` |
| 155 | + |
| 156 | +再分享 [StefanPochmann](https://leetcode.com/problems/range-sum-query-2d-immutable/discuss/75381/C%2B%2B-with-helper) 大神的一个思路,上边我们用 `matrixAccumulate[i][j]` 来保存从 `(0, 0)` 到 `(i - 1, j - 1)` 矩阵内所有数累计的和,多了减一。虽然这种思路经常用到,就像字符串截取函数一样,一般都是包括左端点,不包括右端点,但看起来比较绕。 |
| 157 | + |
| 158 | +我们可以用 `matrixAccumulate[i][j]` 来保存从 `(0, 0)` 到 `(i, j)` 矩阵内所有数累计的和,这样的话,为了避免单独判断边界情况的麻烦,我们可以封装一个函数,对于下标小于 `0` 的边界情况直接返回 `0` ,参考下边的代码。 |
| 159 | + |
| 160 | +```java |
| 161 | +/** |
| 162 | + * @param {number[][]} matrix |
| 163 | + */ |
| 164 | +var NumMatrix = function (matrix) { |
| 165 | + this.matrixAccumulate = matrix; |
| 166 | + let rows = matrix.length; |
| 167 | + if (rows === 0) { |
| 168 | + return; |
| 169 | + } |
| 170 | + let cols = matrix[0].length; |
| 171 | + |
| 172 | + for (let i = 0; i < rows; i++) { |
| 173 | + for (let j = 0; j < cols; j++) { |
| 174 | + this.matrixAccumulate[i][j] += |
| 175 | + this.f(i - 1, j) + this.f(i, j - 1) - this.f(i - 1, j - 1); |
| 176 | + } |
| 177 | + } |
| 178 | +}; |
| 179 | + |
| 180 | +/** |
| 181 | + * @param {number} row1 |
| 182 | + * @param {number} col1 |
| 183 | + * @param {number} row2 |
| 184 | + * @param {number} col2 |
| 185 | + * @return {number} |
| 186 | + */ |
| 187 | +NumMatrix.prototype.sumRegion = function (row1, col1, row2, col2) { |
| 188 | + return ( |
| 189 | + this.f(row2, col2) - |
| 190 | + this.f(row1 - 1, col2) - |
| 191 | + this.f(row2, col1 - 1) + |
| 192 | + this.f(row1 - 1, col1 - 1) |
| 193 | + ); |
| 194 | +}; |
| 195 | + |
| 196 | +NumMatrix.prototype.f = function (i, j) { |
| 197 | + return i >= 0 && j >= 0 ? this.matrixAccumulate[i][j] : 0; |
| 198 | +}; |
| 199 | + |
| 200 | +/** |
| 201 | + * Your NumMatrix object will be instantiated and called as such: |
| 202 | + * var obj = new NumMatrix(matrix) |
| 203 | + * var param_1 = obj.sumRegion(row1,col1,row2,col2) |
| 204 | + */ |
| 205 | +``` |
| 206 | + |
| 207 | +# 总 |
| 208 | + |
| 209 | +比较简单的一道题,基本上还是上一题的思路,想起来小学求矩形面积了,哈哈。解法二的话两种技巧都是处理边界情况的方法,将边界的逻辑和其他部分的逻辑统一了起来,前一种扩充 `0` 的技巧比较常用。 |
0 commit comments