Introduction
LeetCode 308: Range Sum Query 2D - Mutable is an extension of the Range Sum Query - Mutable problem, where instead of a one-dimensional array, you are given a two-dimensional array. The goal is to efficiently handle both updates to individual elements and range sum queries over a submatrix. A brute-force approach would involve recalculating the sum of the elements in the submatrix for each query, but we can use an advanced data structure like a 2D Fenwick Tree (Binary Indexed Tree, BIT) to efficiently update elements and calculate the sum of any submatrix.
Problem Statement
Given a 2D matrix matrix of integers, implement a data structure that supports the following two operations:
- update(row, col, val): Update the element at position
(row, col) to val.
- sumRegion(row1, col1, row2, col2): Return the sum of the elements of the submatrix from
(row1, col1) to (row2, col2) (inclusive).
Both operations should be handled efficiently.
Example
python
# Example 1
Input:
["NumMatrix", "sumRegion", "update", "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], [3, 3, 2], [0, 0, 1, 1]]
Output:
[null, 8, null, 10]
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); // returns 8 (sum of the submatrix from (2,1) to (4,3) = 8)
numMatrix.update(3, 3, 2); // matrix[3][3] = 2
numMatrix.sumRegion(0, 0, 2, 4); // returns 10 (sum of the submatrix from (0,0) to (2,4) = 10)
Step-by-Step Solution
๐ง Intuition
This problem involves two operations on a 2D array:
- Range Sum Queries: Efficiently calculate the sum of elements in a submatrix (from
(row1, col1) to (row2, col2)).
- Updates: Modify the value at a specific position and adjust the data structure to reflect this change.
A brute-force approach that recalculates the sum for each query would be inefficient. Instead, we can use a 2D Fenwick Tree (BIT), which is a data structure that efficiently supports both point updates and range sum queries.
โ
Approach
- 2D Fenwick Tree (Binary Indexed Tree):
- The Fenwick Tree is extended to handle 2D range sums.
- The idea is to maintain a tree structure where each element stores the sum of a specific region of the matrix. Using this structure, both updates and sum queries can be processed in O(log n * log m) time, where
n is the number of rows and m is the number of columns in the matrix.
- Operations:
- update(row, col, val): To update the value at
(row, col), calculate the difference between the new value (val) and the old value, and propagate this difference through the 2D Fenwick Tree.
- sumRegion(row1, col1, row2, col2): To compute the sum of the submatrix from
(row1, col1) to (row2, col2), we query the Fenwick Tree for the sums of four regions and combine them to get the required sum.
- 2D Fenwick Tree Structure:
- The 2D Fenwick Tree stores cumulative sums over rectangular regions of the matrix.
- The update operation modifies values at a specific location and propagates the change through the tree.
- The sum operation queries the tree to find the sum of values within a rectangular region.
โ
Python Code
python
class NumMatrix:
def __init__(self, matrix: list[list[int]]):
if not matrix or not matrix[0]:
return
self.m, self.n = len(matrix), len(matrix[0])
self.bit = [[0] * (self.n + 1) for _ in range(self.m + 1)]
# Initialize Fenwick Tree (BIT)
for i in range(self.m):
for j in range(self.n):
self.update(i, j, matrix[i][j])
def update(self, row: int, col: int, val: int) -> None:
# Update the BIT at (row, col) with new value val
diff = val - self.sumRegion(row, col, row, col)
row += 1
col += 1
while row <= self.m:
temp_col = col
while temp_col <= self.n:
self.bit[row][temp_col] += diff
temp_col += temp_col & -temp_col
row += row & -row
def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
# Return the sum of the region (row1, col1) to (row2, col2)
return (self.query(row2, col2) - self.query(row1 - 1, col2)
- self.query(row2, col1 - 1) + self.query(row1 - 1, col1 - 1))
def query(self, row: int, col: int) -> int:
# Get the sum of the region (1, 1) to (row, col)
row += 1
col += 1
total = 0
while row > 0:
temp_col = col
while temp_col > 0:
total += self.bit[row][temp_col]
temp_col -= temp_col & -temp_col
row -= row & -row
return total
๐งช How It Works
- Constructor (
__init__):
- The constructor initializes the
matrix and creates the 2D Fenwick Tree (bit) to manage the range sums.
- It initializes the Fenwick Tree by iterating over the matrix and updating the tree with each element using the
update method.
update(row, col, val):
- The
update method calculates the difference between the new value (val) and the current value at position (row, col).
- This difference is then propagated through the 2D Fenwick Tree by adjusting the relevant entries in the tree.
sumRegion(row1, col1, row2, col2):
- To compute the sum of a submatrix from
(row1, col1) to (row2, col2), the method queries the Fenwick Tree for four rectangular sums and combines them to get the desired result.
query(row, col):
- The
query method computes the prefix sum of the matrix from (1, 1) to (row, col) using the 2D Fenwick Tree.
โฑ๏ธ Time and Space Complexity
MetricValueTime Complexity (update)O(log n * log m)Time Complexity (sumRegion)O(log n * log m)Space ComplexityO(n * m)
- Time Complexity (update): The update operation involves adjusting values in the Fenwick Tree, which takes O(log n * log m) time, where
n is the number of rows and m is the number of columns in the matrix.
- Time Complexity (sumRegion): The sum region query is answered in O(log n * log m) time by querying the Fenwick Tree.
- Space Complexity: The space complexity is O(n * m), where
n is the number of rows and m is the number of columns in the matrix, as we store the 2D Fenwick Tree.
๐ Edge Cases
- Empty Matrix:
- If the matrix is empty, ensure that all operations are handled gracefully, especially during initialization.
- Multiple Updates to Same Cell:
- If the same cell is updated multiple times, the algorithm should handle this correctly by adjusting the 2D Fenwick Tree.
- Small Matrices:
- Ensure that queries and updates on small matrices (e.g., 1x1 or 2x2) work without errors.
โ
Conclusion
LeetCode 308: Range Sum Query 2D - Mutable is an extension of the 1D problem to a 2D matrix. Using a 2D Fenwick Tree allows us to efficiently handle both point updates and range sum queries in O(log n * log m) time. This solution provides an efficient way to work with mutable 2D arrays, ensuring that even with many queries and updates, the solution remains performant.