You are given a 2D matrix matrix, handle multiple queries of the following type:
sum of the elements of matrix inside 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 matrix matrix.int sumRegion(int row1, int col1, int row2, int col2) Returns the sum of the elements of matrix inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2).sumRegion works on O(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 < n10,000 calls will be made to sumRegion.Before attempting this problem, you should be comfortable with:
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.
sumRegion(row1, col1, row2, col2) query:res = 0.row1 to row2.col1 to col2.res.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 resWhere is the number of rows and is the number of columns in the matrix.
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.
prefixSum[row][col] holds the cumulative sum of row row from column 0 to col.sumRegion(row1, col1, row2, col2) query:res = 0.row1 to row2:prefixSum[row][col2] to res.col1 > 0, subtract prefixSum[row][col1 - 1] from res.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 resWhere is the number of rows and is the number of columns in the matrix.
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).
sumMat of size (ROWS + 1) x (COLS + 1) initialized to zero.r, maintain a running prefix sum across columns.sumMat[r + 1][c + 1] = prefix + sumMat[r][c + 1].sumRegion(row1, col1, row2, col2) query:bottomRight = sumMat[row2 + 1][col2 + 1].above = sumMat[row1][col2 + 1].left = sumMat[row2 + 1][col1].topLeft = sumMat[row1][col1].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 + topLeftWhere is the number of rows and is the number of columns in the matrix.
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.
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.
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.