Skip to content

Commit 8335e1e

Browse files
authored
Merge pull request #4264 from SanskarSinghiit/master2
Adds Leetcode 308 solution 🧩 and addresses changes requested
2 parents ee72cee + 65aab07 commit 8335e1e

File tree

1 file changed

+333
-0
lines changed

1 file changed

+333
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
---
2+
id: range-sum-query-2d-mutable
3+
title: Range Sum Query 2D - Mutable
4+
sidebar_label: 0308-Range-Sum-Query-2D-Mutable
5+
tags: [Segment Tree, 2D Matrix, Hard]
6+
description: Given a 2D matrix, handle multiple queries of the following types - updating the value of a cell and calculating the sum of elements inside a rectangle defined by its upper left and lower right corners.
7+
8+
---
9+
10+
11+
## Problem Statement
12+
13+
Given a 2D matrix `matrix`, handle multiple queries of the following types:
14+
15+
1. **Update** the value of a cell in `matrix`.
16+
2. **Calculate** the sum of the elements inside a rectangle defined by its upper left corner `(row1, col1)` and lower right corner `(row2, col2)`.
17+
18+
Implement the `NumMatrix` class:
19+
20+
- **NumMatrix(int[][] matrix)**: Initializes the object with the integer matrix `matrix`.
21+
- **void update(int row, int col, int val)**: Updates the value of `matrix[row][col]` to be `val`.
22+
- **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)`.
23+
24+
### Examples
25+
26+
**Example 1:**
27+
28+
```plaintext
29+
Input:
30+
["NumMatrix", "sumRegion", "update", "sumRegion"]
31+
[[[[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, 2, 2], [2, 1, 4, 3]]
32+
Output:
33+
[null, 8, null, 10]
34+
```
35+
36+
**Example 2:**
37+
38+
```plaintext
39+
Input: m = 1, n = 1, positions = [[0,0]]
40+
Output: [1]
41+
```
42+
43+
### Constraints
44+
- m == matrix.length
45+
- n == matrix[i].length
46+
- 1 <= m, n <= 200
47+
- -1000 <= matrix[i][j] <= 1000
48+
- 0 <= row < m
49+
- 0 <= col < n
50+
- -1000 <= val <= 1000
51+
- 0 <= row1 <= row2 < m
52+
- 0 <= col1 <= col2 < n
53+
- At most 5000 calls will be made to sumRegion and update.
54+
55+
### Follow up
56+
Implement a solution with Binary Indexed Tree or Segment Tree.
57+
58+
## Solution
59+
60+
### Approach
61+
62+
We use Binary Indexed Tree (BIT) to handle the update and sum queries efficiently.
63+
64+
#### Algorithm
65+
- Binary Indexed Tree: Maintain a BIT to manage and update the sums of elements efficiently.
66+
- Update Operation: Update the value in the BIT when a cell value changes.
67+
- Query Operation: Calculate the sum for a given rectangle using the BIT.
68+
69+
#### Python
70+
71+
```py
72+
# segment tree
73+
class Node:
74+
def __init__(self):
75+
self.l = 0
76+
self.r = 0
77+
self.v = 0
78+
79+
class SegmentTree:
80+
def __init__(self, nums):
81+
n = len(nums)
82+
self.nums = nums
83+
self.tr = [Node() for _ in range(4 * n)]
84+
self.build(1, 1, n)
85+
86+
def build(self, u, l, r):
87+
self.tr[u].l = l
88+
self.tr[u].r = r
89+
if l == r:
90+
self.tr[u].v = self.nums[l - 1]
91+
return
92+
mid = (l + r) >> 1
93+
self.build(u << 1, l, mid)
94+
self.build(u << 1 | 1, mid + 1, r)
95+
self.pushup(u)
96+
97+
def modify(self, u, x, v):
98+
if self.tr[u].l == x and self.tr[u].r == x:
99+
self.tr[u].v = v
100+
return
101+
mid = (self.tr[u].l + self.tr[u].r) >> 1
102+
if x <= mid:
103+
self.modify(u << 1, x, v)
104+
else:
105+
self.modify(u << 1 | 1, x, v)
106+
self.pushup(u)
107+
108+
def query(self, u, l, r):
109+
if self.tr[u].l >= l and self.tr[u].r <= r:
110+
return self.tr[u].v
111+
mid = (self.tr[u].l + self.tr[u].r) >> 1
112+
v = 0
113+
if l <= mid:
114+
v += self.query(u << 1, l, r)
115+
if r > mid:
116+
v += self.query(u << 1 | 1, l, r)
117+
return v
118+
119+
def pushup(self, u):
120+
self.tr[u].v = self.tr[u << 1].v + self.tr[u << 1 | 1].v
121+
122+
class NumMatrix:
123+
124+
def __init__(self, matrix: List[List[int]]):
125+
self.trees = [SegmentTree(row) for row in matrix]
126+
127+
def update(self, row: int, col: int, val: int) -> None:
128+
tree = self.trees[row]
129+
tree.modify(1, col + 1, val)
130+
131+
def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
132+
return sum(self.trees[row].query(1, col1 + 1, col2 + 1) for row in range(row1, row2 + 1))
133+
134+
135+
# Your NumMatrix object will be instantiated and called as such:
136+
# obj = NumMatrix(matrix)
137+
# obj.update(row,col,val)
138+
# param_2 = obj.sumRegion(row1,col1,row2,col2)
139+
140+
############
141+
'''
142+
It uses a binary indexed tree (BIT) or Fenwick tree to efficiently update and query sums of submatrices.
143+
The NumMatrix class constructor initializes the BIT and matrix data structure.
144+
The update method updates the matrix and BIT with the difference in values.
145+
The sumRegion method computes the sum of a submatrix using prefix sums computed with the BIT.
146+
The sum method computes a prefix sum in the BIT.
147+
148+
149+
"Fenwick tree" vs "Segment tree"
150+
https://stackoverflow.com/questions/64190332/fenwick-tree-vs-segment-tree
151+
152+
'''
153+
154+
class NumMatrix:
155+
def __init__(self, matrix: List[List[int]]):
156+
if not matrix or not matrix[0]:
157+
self.m, self.n = 0, 0
158+
return
159+
160+
self.m, self.n = len(matrix), len(matrix[0])
161+
self.bit = [[0] * (self.n + 1) for _ in range(self.m + 1)]
162+
self.matrix = [[0] * self.n for _ in range(self.m)]
163+
164+
for i in range(self.m):
165+
for j in range(self.n):
166+
self.update(i, j, matrix[i][j])
167+
168+
def update(self, row: int, col: int, val: int) -> None:
169+
diff = val - self.matrix[row][col]
170+
self.matrix[row][col] = val
171+
i = row + 1
172+
while i <= self.m:
173+
j = col + 1
174+
while j <= self.n:
175+
self.bit[i][j] += diff
176+
j += j & -j
177+
i += i & -i
178+
179+
def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
180+
return self.sum(row2 + 1, col2 + 1) - self.sum(row2 + 1, col1) - self.sum(row1, col2 + 1) + self.sum(row1, col1)
181+
182+
def sum(self, row: int, col: int) -> int:
183+
res = 0
184+
i = row
185+
while i > 0:
186+
j = col
187+
while j > 0:
188+
res += self.bit[i][j]
189+
j -= j & -j
190+
i -= i & -i
191+
return res
192+
193+
```
194+
195+
#### Java
196+
197+
```java
198+
class BinaryIndexedTree {
199+
private int n;
200+
private int[] c;
201+
202+
public BinaryIndexedTree(int n) {
203+
this.n = n;
204+
c = new int[n + 1];
205+
}
206+
207+
public void update(int x, int delta) {
208+
while (x <= n) {
209+
c[x] += delta;
210+
x += lowbit(x);
211+
}
212+
}
213+
214+
public int query(int x) {
215+
int s = 0;
216+
while (x > 0) {
217+
s += c[x];
218+
x -= lowbit(x);
219+
}
220+
return s;
221+
}
222+
223+
public static int lowbit(int x) {
224+
return x & -x;
225+
}
226+
}
227+
228+
class NumMatrix {
229+
private BinaryIndexedTree[] trees;
230+
231+
public NumMatrix(int[][] matrix) {
232+
int m = matrix.length;
233+
int n = matrix[0].length;
234+
trees = new BinaryIndexedTree[m];
235+
for (int i = 0; i < m; ++i) {
236+
BinaryIndexedTree tree = new BinaryIndexedTree(n);
237+
for (int j = 0; j < n; ++j) {
238+
tree.update(j + 1, matrix[i][j]);
239+
}
240+
trees[i] = tree;
241+
}
242+
}
243+
244+
public void update(int row, int col, int val) {
245+
BinaryIndexedTree tree = trees[row];
246+
int prev = tree.query(col + 1) - tree.query(col);
247+
tree.update(col + 1, val - prev);
248+
}
249+
250+
public int sumRegion(int row1, int col1, int row2, int col2) {
251+
int s = 0;
252+
for (int i = row1; i <= row2; ++i) {
253+
BinaryIndexedTree tree = trees[i];
254+
s += tree.query(col2 + 1) - tree.query(col1);
255+
}
256+
return s;
257+
}
258+
}
259+
260+
```
261+
262+
#### C++
263+
264+
```cpp
265+
class BinaryIndexedTree {
266+
public:
267+
int n;
268+
vector<int> c;
269+
270+
BinaryIndexedTree(int _n)
271+
: n(_n)
272+
, c(_n + 1) {}
273+
274+
void update(int x, int delta) {
275+
while (x <= n) {
276+
c[x] += delta;
277+
x += lowbit(x);
278+
}
279+
}
280+
281+
int query(int x) {
282+
int s = 0;
283+
while (x > 0) {
284+
s += c[x];
285+
x -= lowbit(x);
286+
}
287+
return s;
288+
}
289+
290+
int lowbit(int x) {
291+
return x & -x;
292+
}
293+
};
294+
295+
class NumMatrix {
296+
public:
297+
vector<BinaryIndexedTree*> trees;
298+
299+
NumMatrix(vector<vector<int>>& matrix) {
300+
int m = matrix.size();
301+
int n = matrix[0].size();
302+
trees.resize(m);
303+
for (int i = 0; i < m; ++i) {
304+
BinaryIndexedTree* tree = new BinaryIndexedTree(n);
305+
for (int j = 0; j < n; ++j) {
306+
tree->update(j + 1, matrix[i][j]);
307+
}
308+
trees[i] = tree;
309+
}
310+
}
311+
312+
void update(int row, int col, int val) {
313+
BinaryIndexedTree* tree = trees[row];
314+
int prev = tree->query(col + 1) - tree->query(col);
315+
tree->update(col + 1, val - prev);
316+
}
317+
318+
int sumRegion(int row1, int col1, int row2, int col2) {
319+
int s = 0;
320+
for (int i = row1; i <= row2; ++i) {
321+
BinaryIndexedTree* tree = trees[i];
322+
s += tree->query(col2 + 1) - tree->query(col1);
323+
}
324+
return s;
325+
}
326+
};
327+
328+
```
329+
330+
### Complexity Analysis
331+
332+
- **Time Complexity**: $O(\log m \cdot \log n)$ for both update and sumRegion operations, where $m$ and $n$ are the dimensions of the matrix.
333+
- **Space Complexity**: $O(m \cdot n)$ for storing the BITs and matrix.

0 commit comments

Comments
 (0)