You are given an n x n binary matrix grid where 1 represents land and 0 represents water.
An island is a 4-directionally connected group of 1's not connected to any other 1's. There are exactly two islands in grid.
You may change 0's to 1's to connect the two islands to form one island.
Return the minimum number of 0's you must flip to connect the two islands.
Example 1:
Input: grid = [
[1,0],
[0,1]
]
Output: 1Example 2:
Input: grid = [
[0,1,0],
[0,0,0],
[0,0,1]
]
Output: 2Example 3:
Input: grid = [
[1,1,1,1,1],
[1,0,0,0,1],
[0,1,1,0,1],
[1,0,0,0,1],
[1,1,1,1,1]
]
Output: 1Constraints:
2 <= grid.length == grid[i].length <= 100grid[i][j] is either 0 or 1.grid.Before attempting this problem, you should be comfortable with:
The problem asks for the minimum number of 0s to flip to connect two islands. This is essentially finding the shortest path between the two islands across water.
First, we identify one island completely using DFS and mark all its cells as visited. Then, we use BFS to expand outward from this island. BFS naturally finds the shortest path because it explores all cells at distance 1 before distance 2, and so on. The moment we reach a cell belonging to the second island, we have found the minimum bridge length.
DFS to mark all cells of the first island as visited.BFS queue with all cells from the first island.BFS, expanding outward level by level:class Solution:
def shortestBridge(self, grid: List[List[int]]) -> int:
N = len(grid)
direct = [[0, 1], [0, -1], [1, 0], [-1, 0]]
def invalid(r, c):
return r < 0 or c < 0 or r == N or c == N
visit = set()
def dfs(r, c):
if invalid(r, c) or not grid[r][c] or (r, c) in visit:
return
visit.add((r, c))
for dr, dc in direct:
dfs(r + dr, c + dc)
def bfs():
res, q = 0, deque(visit)
while q:
for _ in range(len(q)):
r, c = q.popleft()
for dr, dc in direct:
curR, curC = r + dr, c + dc
if invalid(curR, curC) or (curR, curC) in visit:
continue
if grid[curR][curC]:
return res
q.append((curR, curC))
visit.add((curR, curC))
res += 1
for r in range(N):
for c in range(N):
if grid[r][c]:
dfs(r, c)
return bfs()This is a space-optimized version of the previous approach. Instead of using a separate visited set, we modify the grid directly by marking visited land cells with the value 2. This distinguishes them from unvisited land (value 1) and water (value 0).
During BFS expansion, water cells are also marked as 2 when visited. When we encounter a cell with value 1, we know it belongs to the second island.
DFS to mark all its cells with value 2.DFS, add each cell to the BFS queue.BFS, expanding outward level by level:1, return the current distance (found second island).0, mark it as 2 and add to the queue.class Solution:
def shortestBridge(self, grid: list[list[int]]) -> int:
N, direct = len(grid), [(0, 1), (0, -1), (1, 0), (-1, 0)]
def dfs(r, c):
if 0 <= r < N and 0 <= c < N and grid[r][c] == 1:
grid[r][c] = 2
q.append((r, c))
for dr, dc in direct:
dfs(r + dr, c + dc)
q = deque()
for r in range(N):
for c in range(N):
if grid[r][c]:
dfs(r, c)
break
if q: break
res = 0
while q:
for _ in range(len(q)):
r, c = q.popleft()
for dr, dc in direct:
nr, nc = r + dr, c + dc
if 0 <= nr < N and 0 <= nc < N:
if grid[nr][nc] == 1:
return res
if grid[nr][nc] == 0:
grid[nr][nc] = 2
q.append((nr, nc))
res += 1We can use BFS for both phases: identifying the first island and expanding to find the bridge. This avoids the recursive overhead of DFS and may be preferable for very large islands.
The first BFS explores all connected land cells starting from the first land cell found, marking them as visited. The second BFS then expands from all boundary cells of the first island simultaneously, searching for the second island.
BFS from this cell to identify all cells of the first island, marking them as 2 and adding them to a second queue.BFS using the second queue, expanding outward level by level:1, return the current distance.0, mark it as 2 and add to the queue.class Solution:
def shortestBridge(self, grid: list[list[int]]) -> int:
N, direct = len(grid), [(0, 1), (0, -1), (1, 0), (-1, 0)]
q2 = deque()
found = False
for r in range(N):
if found: break
for c in range(N):
if grid[r][c] == 1:
q1 = deque([(r, c)])
grid[r][c] = 2
while q1:
x, y = q1.popleft()
q2.append((x, y))
for dx, dy in direct:
nx, ny = x + dx, y + dy
if 0 <= nx < N and 0 <= ny < N and grid[nx][ny] == 1:
grid[nx][ny] = 2
q1.append((nx, ny))
found = True
break
res = 0
while q2:
for _ in range(len(q2)):
x, y = q2.popleft()
for dx, dy in direct:
nx, ny = x + dx, y + dy
if 0 <= nx < N and 0 <= ny < N:
if grid[nx][ny] == 1:
return res
if grid[nx][ny] == 0:
grid[nx][ny] = 2
q2.append((nx, ny))
res += 1
return resA Disjoint Set Union (DSU) data structure can identify connected components. By scanning the grid and unioning adjacent land cells, we naturally group cells into their respective islands.
Once we know which cells belong to the first island (tracked during the initial union phase), we start BFS from the boundary cells of that island. As we expand, we union newly visited cells with the first island. When a union operation connects to a cell that was already land (value 1) but in a different component, we have found the bridge.
DSU with n * n + 1 elements.BFS queue.BFS, expanding outward level by level:true (different component), return the current distance.class DSU:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [1] * n
def find(self, node):
cur = node
while cur != self.parent[cur]:
self.parent[cur] = self.parent[self.parent[cur]]
cur = self.parent[cur]
return cur
def union(self, u, v):
pu, pv = self.find(u), self.find(v)
if pu == pv:
return False
if self.rank[pv] > self.rank[pu]:
pu, pv = pv, pu
self.parent[pv] = pu
self.rank[pu] += self.rank[pv]
return True
class Solution:
def shortestBridge(self, grid: list[list[int]]) -> int:
n, direct = len(grid), [(0, 1), (0, -1), (1, 0), (-1, 0)]
dsu = DSU(n * n + 1)
def idx(r, c):
return r * n + c + 1
for r in range(n):
for c in range(n):
if grid[r][c] == 1:
first_island = dsu.find(idx(r, c))
if c + 1 < n and grid[r][c + 1] == 1:
dsu.union(idx(r, c), idx(r, c + 1))
if r + 1 < n and grid[r + 1][c] == 1:
dsu.union(idx(r, c), idx(r + 1, c))
q = deque()
for r in range(n):
for c in range(n):
if grid[r][c] == 1:
if dsu.find(idx(r, c)) != first_island:
continue
for dx, dy in direct:
nr, nc = r + dx, c + dy
if 0 <= nr < n and 0 <= nc < n and grid[nr][nc] == 0:
q.append((r,c))
break
res = 0
while q:
for _ in range(len(q)):
r, c = q.popleft()
for dx, dy in direct:
nr, nc = r + dx, c + dy
if 0 <= nr < n and 0 <= nc < n:
if grid[nr][nc] == 1 and dsu.union(idx(r, c), idx(nr, nc)):
return res
if grid[nr][nc] == 0:
grid[nr][nc] = 1
dsu.union(idx(r, c), idx(nr, nc))
q.append((nr, nc))
res += 1A common mistake is marking cells as visited only when dequeuing rather than when enqueuing. This causes the same cell to be added to the queue multiple times from different neighbors, leading to incorrect distance calculations and potential TLE. Always mark a cell as visited immediately when adding it to the BFS queue.
The DFS phase identifies all cells of the first island, while the BFS phase finds the shortest path to the second island. Mixing up these phases or using incorrect termination conditions (e.g., stopping DFS when finding any land cell) will produce wrong results. The BFS should return immediately upon reaching any cell with value 1 that was not part of the first island.
When BFS reaches a cell of the second island, the current distance or steps counter represents the number of water cells flipped, which is the bridge length. Some implementations incorrectly return distance + 1 or forget that the starting cells of BFS (the first island's boundary) should not count toward the bridge length. The bridge length is the number of 0s between the islands, not including the island cells themselves.