Skip to content

Commit ee72cee

Browse files
authored
Merge pull request #4263 from SanskarSinghiit/master1
Adds Leetcode 305 solution 🧩 and addresses changes requested
2 parents 7275563 + 6b298ab commit ee72cee

File tree

1 file changed

+341
-0
lines changed

1 file changed

+341
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
---
2+
id: number-of-islands-ii
3+
title: Number of Islands II
4+
sidebar_label: 0305-Number-of-Islands-II
5+
tags: [Union-Find, Matrix, Hard]
6+
description: Given a 2D grid map of `m` rows and `n` columns initially filled with water, perform `addLand` operations that turn the water at specific positions into land. Return the number of islands after each operation. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically.
7+
8+
---
9+
10+
## Problem Statement
11+
12+
You are given an empty 2D binary grid `grid` of size `m x n`. The grid represents a map where `0`s represent water and `1`s represent land. Initially, all the cells of `grid` are water cells (i.e., all the cells are `0`s).
13+
14+
We may perform an `addLand` operation which turns the water at position `(ri, ci)` into land. You are given an array `positions` where `positions[i] = [ri, ci]` is the position `(ri, ci)` at which we should operate the `i`-th operation.
15+
16+
Return an array of integers `answer` where `answer[i]` is the number of islands after turning the cell `(ri, ci)` into a land.
17+
18+
An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
19+
20+
### Examples
21+
22+
**Example 1:**
23+
24+
```plaintext
25+
Input: m = 3, n = 3, positions = [[0,0],[0,1],[1,2],[2,1]]
26+
Output: [1,1,2,3]
27+
Explanation:
28+
Initially, the 2D grid is filled with water.
29+
- Operation #1: addLand(0, 0) turns the water at grid[0][0] into a land. We have 1 island.
30+
- Operation #2: addLand(0, 1) turns the water at grid[0][1] into a land. We still have 1 island.
31+
- Operation #3: addLand(1, 2) turns the water at grid[1][2] into a land. We have 2 islands.
32+
- Operation #4: addLand(2, 1) turns the water at grid[2][1] into a land. We have 3 islands.
33+
```
34+
35+
**Example 2:**
36+
37+
```plaintext
38+
Input: m = 1, n = 1, positions = [[0,0]]
39+
Output: [1]
40+
```
41+
42+
### Constraints
43+
- 1 <= m, n, positions.length <= 10^4
44+
- 1 <= m * n <= 10^4
45+
- positions[i].length == 2
46+
- 0 <= ri < m
47+
- 0 <= ci < n
48+
49+
### Follow up
50+
Could you solve it in time complexity $O(k log(mn))$, where k == positions.length?
51+
52+
## Solution
53+
54+
### Approach
55+
56+
We use Union-Find to keep track of connected components (islands) and perform operations efficiently.
57+
58+
#### Algorithm
59+
- Union-Find Data Structure: Used to manage and merge different islands.
60+
- Add Land Operation: Convert the water at a given position into land and update the number of islands by checking adjacent cells and merging islands as needed.
61+
62+
#### Python
63+
64+
```py
65+
class Solution:
66+
def numIslands2(self, m: int, n: int, positions: List[List[int]]) -> List[int]:
67+
def check(i, j):
68+
return 0 <= i < m and 0 <= j < n and grid[i][j] == 1
69+
70+
def find(x):
71+
if p[x] != x:
72+
p[x] = find(p[x])
73+
return p[x]
74+
75+
p = list(range(m * n))
76+
grid = [[0] * n for _ in range(m)]
77+
res = []
78+
cur = 0 # current island count
79+
for i, j in positions:
80+
if grid[i][j] == 1: # already counted, same as previous island count
81+
res.append(cur)
82+
continue
83+
grid[i][j] = 1
84+
cur += 1
85+
for x, y in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
86+
if check(i + x, j + y) and find(i * n + j) != find((i + x) * n + j + y):
87+
p[find(i * n + j)] = find((i + x) * n + j + y)
88+
cur -= 1
89+
# 1 0 1
90+
# 0 1 0
91+
# for above, if setting 1st-row 2nd-col 0-to-1
92+
# cur will -1 for 3 times
93+
# but cur += 1 after grid[i][j] = 1 so cur final result is 1
94+
res.append(cur)
95+
return res
96+
97+
############
98+
99+
class UnionFind(object):
100+
def __init__(self, m, n):
101+
self.dad = [i for i in range(m * n)]
102+
self.rank = [0 for _ in range(m * n)]
103+
104+
def find(self, x):
105+
if self.dad[x] != x:
106+
self.dad[x] = self.find(self.dad[x])
107+
return self.dad[x]
108+
109+
def union(self, xy): # xy is a tuple (x,y), with x and y value inside
110+
x, y = map(self.find, xy)
111+
if x == y:
112+
return False
113+
if self.rank[x] > self.rank[y]:
114+
self.dad[y] = x
115+
elif self.rank[x] < self.rank[y]:
116+
self.dad[x] = y
117+
else:
118+
self.dad[y] = x # search to the left, to find parent
119+
self.rank[x] += 1 # now x a parent, so +1 its rank
120+
return True
121+
122+
123+
class Solution(object):
124+
def numIslands2(self, m, n, positions):
125+
"""
126+
:type m: int
127+
:type n: int
128+
:type positions: List[List[int]]
129+
:rtype: List[int]
130+
"""
131+
uf = UnionFind(m, n)
132+
ans = 0
133+
ret = []
134+
dirs = [(0, -1), (0, 1), (1, 0), (-1, 0)]
135+
grid = set()
136+
for i, j in positions:
137+
ans += 1
138+
grid |= {(i, j)}
139+
for di, dj in dirs:
140+
ni, nj = i + di, j + dj
141+
if 0 <= ni < m and 0 <= nj < n and (ni, nj) in grid:
142+
if uf.union((ni * n + nj, i * n + j)):
143+
ans -= 1
144+
ret.append(ans)
145+
return ret
146+
147+
148+
################
149+
150+
151+
class UnionFind:
152+
def __init__(self, n: int):
153+
self.p = list(range(n))
154+
self.size = [1] * n
155+
156+
def find(self, x: int):
157+
if self.p[x] != x:
158+
self.p[x] = self.find(self.p[x])
159+
return self.p[x]
160+
161+
def union(self, a: int, b: int) -> bool:
162+
pa, pb = self.find(a - 1), self.find(b - 1)
163+
if pa == pb:
164+
return False
165+
if self.size[pa] > self.size[pb]:
166+
self.p[pb] = pa
167+
self.size[pa] += self.size[pb]
168+
else:
169+
self.p[pa] = pb
170+
self.size[pb] += self.size[pa]
171+
return True
172+
173+
174+
class Solution:
175+
def numIslands2(self, m: int, n: int, positions: List[List[int]]) -> List[int]:
176+
uf = UnionFind(m * n)
177+
grid = [[0] * n for _ in range(m)]
178+
ans = []
179+
dirs = (-1, 0, 1, 0, -1)
180+
cnt = 0
181+
for i, j in positions:
182+
if grid[i][j]:
183+
ans.append(cnt)
184+
continue
185+
grid[i][j] = 1
186+
cnt += 1
187+
for a, b in pairwise(dirs):
188+
x, y = i + a, j + b
189+
if (
190+
0 <= x < m
191+
and 0 <= y < n
192+
and grid[x][y]
193+
and uf.union(i * n + j, x * n + y)
194+
):
195+
cnt -= 1
196+
ans.append(cnt)
197+
return ans
198+
199+
```
200+
201+
#### Java
202+
203+
```java
204+
class UnionFind {
205+
private final int[] p;
206+
private final int[] size;
207+
208+
public UnionFind(int n) {
209+
p = new int[n];
210+
size = new int[n];
211+
for (int i = 0; i < n; ++i) {
212+
p[i] = i;
213+
size[i] = 1;
214+
}
215+
}
216+
217+
public int find(int x) {
218+
if (p[x] != x) {
219+
p[x] = find(p[x]);
220+
}
221+
return p[x];
222+
}
223+
224+
public boolean union(int a, int b) {
225+
int pa = find(a), pb = find(b);
226+
if (pa == pb) {
227+
return false;
228+
}
229+
if (size[pa] > size[pb]) {
230+
p[pb] = pa;
231+
size[pa] += size[pb];
232+
} else {
233+
p[pa] = pb;
234+
size[pb] += size[pa];
235+
}
236+
return true;
237+
}
238+
}
239+
240+
class Solution {
241+
public List<Integer> numIslands2(int m, int n, int[][] positions) {
242+
int[][] grid = new int[m][n];
243+
UnionFind uf = new UnionFind(m * n);
244+
int[] dirs = {-1, 0, 1, 0, -1};
245+
int cnt = 0;
246+
List<Integer> ans = new ArrayList<>();
247+
for (var p : positions) {
248+
int i = p[0], j = p[1];
249+
if (grid[i][j] == 1) {
250+
ans.add(cnt);
251+
continue;
252+
}
253+
grid[i][j] = 1;
254+
++cnt;
255+
for (int k = 0; k < 4; ++k) {
256+
int x = i + dirs[k], y = j + dirs[k + 1];
257+
if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1
258+
&& uf.union(i * n + j, x * n + y)) {
259+
--cnt;
260+
}
261+
}
262+
ans.add(cnt);
263+
}
264+
return ans;
265+
}
266+
}
267+
268+
```
269+
270+
#### C++
271+
272+
```cpp
273+
class UnionFind {
274+
public:
275+
UnionFind(int n) {
276+
p = vector<int>(n);
277+
size = vector<int>(n, 1);
278+
iota(p.begin(), p.end(), 0);
279+
}
280+
281+
bool unite(int a, int b) {
282+
int pa = find(a), pb = find(b);
283+
if (pa == pb) {
284+
return false;
285+
}
286+
if (size[pa] > size[pb]) {
287+
p[pb] = pa;
288+
size[pa] += size[pb];
289+
} else {
290+
p[pa] = pb;
291+
size[pb] += size[pa];
292+
}
293+
return true;
294+
}
295+
296+
int find(int x) {
297+
if (p[x] != x) {
298+
p[x] = find(p[x]);
299+
}
300+
return p[x];
301+
}
302+
303+
private:
304+
vector<int> p, size;
305+
};
306+
307+
class Solution {
308+
public:
309+
vector<int> numIslands2(int m, int n, vector<vector<int>>& positions) {
310+
int grid[m][n];
311+
memset(grid, 0, sizeof(grid));
312+
UnionFind uf(m * n);
313+
int dirs[5] = {-1, 0, 1, 0, -1};
314+
int cnt = 0;
315+
vector<int> ans;
316+
for (auto& p : positions) {
317+
int i = p[0], j = p[1];
318+
if (grid[i][j]) {
319+
ans.push_back(cnt);
320+
continue;
321+
}
322+
grid[i][j] = 1;
323+
++cnt;
324+
for (int k = 0; k < 4; ++k) {
325+
int x = i + dirs[k], y = j + dirs[k + 1];
326+
if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] && uf.union(i * n + j, x * n + y)) {
327+
--cnt;
328+
}
329+
}
330+
ans.push_back(cnt);
331+
}
332+
return ans;
333+
}
334+
};
335+
```
336+
337+
### Complexity Analysis
338+
339+
- **Time Complexity**: $O(k \cdot \log(m \cdot n))$, where $k$ is the length of the `positions` array, and $m$ and $n$ are the dimensions of the grid. This is due to the Union-Find operations with path compression and union by size.
340+
341+
- **Space Complexity**: $O(m \cdot n)$, as we need space for the grid and the Union-Find data structure to manage the connectivity of the cells.

0 commit comments

Comments
 (0)