304. Range Sum Query 2D Immutable - Explanation
Description
You are given a 2D matrix matrix, handle multiple queries of the following type:
- Calculate the
sumof the elements ofmatrixinside the rectangle defined by its upper left corner(row1, col1)and lower right corner(row2, col2).
Implement the NumMatrix class:
NumMatrix(int[][] matrix)Initializes the object with the integer matrixmatrix.int sumRegion(int row1, int col1, int row2, int col2)Returns the sum of the elements ofmatrixinside the rectangle defined by its upper left corner(row1, col1)and lower right corner(row2, col2).
You must design an algorithm wheresumRegionworks onO(1)time complexity.
Example 1:
Input: ["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]]
Output: [null, 8, 11, 12]Explanation:
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 (i.e sum of the red rectangle)
numMatrix.sumRegion(1, 1, 2, 2); // return 11 (i.e sum of the green rectangle)
numMatrix.sumRegion(1, 2, 2, 4); // return 12 (i.e sum of the blue rectangle)Constraints:
m == matrix.lengthn == matrix[i].length1 <= m, n <= 200-10,000 <= matrix[i][j] <= 10,0000 <= row1 <= row2 < m0 <= col1 <= col2 < n- At most
10,000calls will be made tosumRegion.
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- 2D Arrays (Matrices) - Working with row and column indices to access and iterate over rectangular regions
- Prefix Sums - Precomputing cumulative sums for constant-time range queries
- Inclusion-Exclusion Principle - Computing 2D region sums by combining overlapping rectangular areas
1. Brute Force
Intuition
The most straightforward approach is to iterate through every cell in the specified rectangular region and sum up all the values. For each query, we simply loop from the top-left corner to the bottom-right corner and accumulate the result. While this is easy to implement, it becomes slow when we have many queries or large regions to sum.
Algorithm
- Store the original matrix.
- For each
sumRegion(row1, col1, row2, col2)query:- Initialize
res = 0. - Iterate through all rows from
row1torow2. - For each row, iterate through all columns from
col1tocol2. - Add each cell value to
res.
- Initialize
- Return
res.
class NumMatrix:
def __init__(self, matrix: list[list[int]]):
self.matrix = matrix
def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
res = 0
for r in range(row1, row2 + 1):
for c in range(col1, col2 + 1):
res += self.matrix[r][c]
return resTime & Space Complexity
- Time complexity: for each query.
- Space complexity:
Where is the number of rows and is the number of columns in the matrix.
2. One Dimensional Prefix Sum
Intuition
Instead of summing every cell for each query, we can precompute prefix sums for each row. Each prefixSum[row][col] stores the sum of all elements from matrix[row][0] to matrix[row][col]. This way, finding the sum of any range within a single row takes constant time. For a rectangular region spanning multiple rows, we sum each row's contribution using its prefix sum.
Algorithm
- Build a 2D prefix sum array where
prefixSum[row][col]holds the cumulative sum of rowrowfrom column0tocol. - For each
sumRegion(row1, col1, row2, col2)query:- Initialize
res = 0. - For each row from
row1torow2:- Add
prefixSum[row][col2]tores. - If
col1 > 0, subtractprefixSum[row][col1 - 1]fromres.
- Add
- Initialize
- Return
res.
class NumMatrix:
def __init__(self, matrix: list[list[int]]):
self.prefixSum = [[0] * len(matrix[0]) for _ in range(len(matrix))]
for row in range(len(matrix)):
self.prefixSum[row][0] = matrix[row][0]
for col in range(1, len(matrix[0])):
self.prefixSum[row][col] = self.prefixSum[row][col - 1] + matrix[row][col]
def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
res = 0
for row in range(row1, row2 + 1):
if col1 > 0:
res += self.prefixSum[row][col2] - self.prefixSum[row][col1 - 1]
else:
res += self.prefixSum[row][col2]
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of rows and is the number of columns in the matrix.
3. Two Dimensional Prefix Sum
Intuition
We can extend prefix sums to two dimensions. The idea is to precompute sumMat[r][c] as the sum of all elements in the rectangle from (0, 0) to (r - 1, c - 1). To find the sum of any rectangular region, we use the inclusion-exclusion principle: take the sum up to the bottom-right corner, subtract the regions above and to the left, then add back the top-left corner (which was subtracted twice).
Algorithm
- Create a prefix sum matrix
sumMatof size(ROWS + 1) x (COLS + 1)initialized to zero. - Build the prefix sum matrix:
- For each row
r, maintain a runningprefixsum across columns. - Set
sumMat[r + 1][c + 1] = prefix + sumMat[r][c + 1].
- For each row
- For each
sumRegion(row1, col1, row2, col2)query:- Compute
bottomRight = sumMat[row2 + 1][col2 + 1]. - Subtract
above = sumMat[row1][col2 + 1]. - Subtract
left = sumMat[row2 + 1][col1]. - Add back
topLeft = sumMat[row1][col1].
- Compute
- Return
bottomRight - above - left + topLeft.
class NumMatrix:
def __init__(self, matrix: list[list[int]]):
ROWS, COLS = len(matrix), len(matrix[0])
self.sumMat = [[0] * (COLS + 1) for _ in range(ROWS + 1)]
for r in range(ROWS):
prefix = 0
for c in range(COLS):
prefix += matrix[r][c]
above = self.sumMat[r][c + 1]
self.sumMat[r + 1][c + 1] = prefix + above
def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
row1, col1, row2, col2 = row1 + 1, col1 + 1, row2 + 1, col2 + 1
bottomRight = self.sumMat[row2][col2]
above = self.sumMat[row1 - 1][col2]
left = self.sumMat[row2][col1 - 1]
topLeft = self.sumMat[row1 - 1][col1 - 1]
return bottomRight - above - left + topLeftTime & Space Complexity
- Time complexity: for each query.
- Space complexity:
Where is the number of rows and is the number of columns in the matrix.
Common Pitfalls
Forgetting the Inclusion-Exclusion Principle
When computing the sum of a rectangular region using 2D prefix sums, you must subtract the regions above and to the left, then add back the top-left corner that was subtracted twice. The formula is bottomRight - above - left + topLeft. Forgetting to add back the top-left corner results in under-counting, while forgetting to subtract either the above or left region causes over-counting.
Off-by-One Errors with Prefix Sum Indices
The prefix sum matrix is typically sized (rows + 1) x (cols + 1) to handle edge cases where the query starts at row 0 or column 0. Mixing up whether indices are 0-based or 1-based leads to accessing wrong cells or out-of-bounds errors. When querying sumRegion(row1, col1, row2, col2), remember to shift indices appropriately when accessing the prefix sum matrix.
Building Prefix Sum Matrix Incorrectly
The 2D prefix sum at position (r, c) should represent the sum of all elements from (0, 0) to (r-1, c-1). A common mistake is computing prefix sums row by row without properly incorporating the contribution from previous rows. The correct formula is prefix[r+1][c+1] = matrix[r][c] + prefix[r][c+1] + prefix[r+1][c] - prefix[r][c], or equivalently, accumulate row prefix and add the cell directly above.
Sign in to join the discussion