Range Sum Query 2D - Immutable
Given a fixed 2D matrix, answer repeated queries for the sum of the values inside the rectangle from (row1, col1) to (row2, col2), inclusive. Build a 2D prefix sum once, where prefixr[c] holds the sum of every cell above and to the left of (r, c). Any rectangle sum is then four lookups and three subtractions — inclusion-exclusion on the corners of the rectangle.
Constraints
- m == matrix.length
- n == matrixi.length
- 1 ≤ m, n ≤ 200
- -105 ≤ matrixi[j] ≤ 105
- 0 ≤ row1 ≤ row2 < m
- 0 ≤ col1 ≤ col2 < n
Example
matrix = [[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]], row1=2, col1=1, row2=4, col2=38Explanation The values inside that rectangle (2 0 1 / 1 0 1 / 0 3 0) sum to 8.
In plain terms
- Inclusion-exclusion
- Adding back a region that got subtracted twice — here, the top-left corner gets removed by both the row-above cut and the column-left cut, so it has to be added back once.
Build a 2D prefix grid, then subtract two strips and add back the corner
The row is the prefix grid for a 2 x 3 matrix: six cells, one per matrix value. All of them start empty.
What happens in this step
matrix = [[3, 0, 1],
[5, 6, 3]]
prefix cell "r,c" = the sum of the first r rows and first c columns of matrix.
So "1,1" covers just matrix[0][0], and "2,3" covers the whole matrix.
Anything with a 0 in its label (a row above the top, or a column left of
the edge) is 0 and never needs storing on screen.Steps to visualize
- The walkthrough uses a smaller matrix than the example above, [[3, 0, 1], [5, 6, 3]], so the whole prefix grid fits on one line.
- The row is that prefix grid, read left to right, top row first. Cell "r,c" holds the sum of every matrix value above and to the left of it — the first r rows and the first c columns. — means not computed yet.
- Build each cell with prefixr[c] = matrix[r-1][c-1] + prefix[r-1][c] + prefixr[c-1] - prefix[r-1][c-1]. Any prefix cell with a 0 in its label is 0, which is why the code keeps a padding row and column.
- To query a rectangle, take the prefix cell at its bottom-right, subtract the strip above it and the strip to its left, then add back the top-left corner that both strips removed.
Walk through the code
Same walkthrough, now with the code. Press Next to move one step and watch which lines run.
The row is the prefix grid for a 2 x 3 matrix: six cells, one per matrix value. All of them start empty.
What happens in this step
matrix = [[3, 0, 1],
[5, 6, 3]]
prefix cell "r,c" = the sum of the first r rows and first c columns of matrix.
So "1,1" covers just matrix[0][0], and "2,3" covers the whole matrix.
Anything with a 0 in its label (a row above the top, or a column left of
the edge) is 0 and never needs storing on screen.Solution
function sumRegion(matrix, row1, col1, row2, col2) {
const rows = matrix.length;
const cols = matrix[0].length;
const prefix = Array.from({ length: rows + 1 }, () => new Array(cols + 1).fill(0));
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
prefix[r + 1][c + 1] = matrix[r][c] + prefix[r][c + 1] + prefix[r + 1][c] - prefix[r][c];
}
}
return (
prefix[row2 + 1][col2 + 1] - prefix[row1][col2 + 1] - prefix[row2 + 1][col1] + prefix[row1][col1]
);
}- Time
- O(rows × cols) to build, O(1) per query
- Space
- O(rows × cols)
Test cases
| Input | Expected | Covers |
|---|---|---|
matrix = [[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]], row1=2, col1=1, row2=4, col2=3 | 8 | example from the docstring |
same matrix, row1=1, col1=1, row2=2, col2=2 | 11 | a second region on the same precomputed prefix grid |
matrix = [[5]], row1=0, col1=0, row2=0, col2=0 | 5 | smallest valid input, a 1x1 matrix |
matrix = [[1,2,3,4]], row1=0, col1=1, row2=0, col2=3 | 9 | a matrix with only one row |
matrix = [[1],[2],[3]], row1=0, col1=0, row2=2, col2=0 | 6 | a matrix with only one column |
matrix = [[-1,2],[3,-4]], row1=0, col1=0, row2=1, col2=1 | 0 | negative values inside the queried region |