题目重点:Uber Phone Screen 的 LC934 变体要求输出最短桥路径。先标记第一座岛,再多源 BFS 并保存水格前驱,返回可翻转的最短坐标链。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 输入为 0/1 矩形网格,恰有两座按四方向相连的岛;1 是陆地,0 是水。
- 输出最少需要翻为陆地的水格坐标列表,按从第一座岛走向第二座岛的顺序;如果有多条最短桥,返回任意一条。
- 题目只说‘打印路径’,未明确是否要含两端陆地;本文训练接口只返回需翻转的水格,若需完整格路径可在两端补陆地坐标。
解题路线
- 找任意陆地格,用 DFS/BFS 标记整座第一岛,将岛上所有格一起放入多源 BFS 队列,距离为 0。
- 从第一岛向水域逐层扩展;首次访问一个水格时只存 previous[水格]=前一格,不复制整条路径。第一次碰到另一座岛时即达到最少翻格数。
- 从接触第二岛前的水格沿 previous 回溯,直到回到第一岛,反转得到水格路径。多源 BFS 的层次保证路径最短。
手动走一遍
网格 [[1,0,1]] 有两座岛,翻 (0,1) 即形成最短桥,代码返回长度为 1 的水格列表。若保存每条队列路径,多个前缀会重复占内存。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
"""Return water cells to flip for a shortest bridge between two islands."""
from collections import deque
def shortest_bridge_path(grid: list[list[int]]) -> list[tuple[int, int]]:
if not grid or not grid[0] or any(len(row) != len(grid[0]) for row in grid):
raise ValueError("nonempty rectangular grid required")
if any(cell not in (0, 1) for row in grid for cell in row):
raise ValueError("grid must contain 0/1")
rows, cols = len(grid), len(grid[0])
def neighbors(row, col):
for dr, dc in ((-1, 0), (1, 0), (0, -1), (0, 1)):
nr, nc = row + dr, col + dc
if 0 <= nr < rows and 0 <= nc < cols:
yield nr, nc
start = next(((r, c) for r in range(rows) for c in range(cols) if grid[r][c] == 1), None)
if start is None:
raise ValueError("no island")
first = {start}
island_queue = deque([start])
while island_queue:
cell = island_queue.popleft()
for neighbor in neighbors(*cell):
if grid[neighbor[0]][neighbor[1]] == 1 and neighbor not in first:
first.add(neighbor)
island_queue.append(neighbor)
queue = deque(first)
seen = set(first)
previous: dict[tuple[int, int], tuple[int, int]] = {}
while queue:
cell = queue.popleft()
for neighbor in neighbors(*cell):
if neighbor in seen:
continue
if grid[neighbor[0]][neighbor[1]] == 1:
path = []
while cell not in first:
path.append(cell)
cell = previous[cell]
return path[::-1]
seen.add(neighbor)
previous[neighbor] = cell
queue.append(neighbor)
raise ValueError("second island not found")
复杂度
第一岛遍历与多源 BFS 每格最多访问一次,时间 O(RC);visited、队列与前驱表最坏 O(RC) 空间。回溯路径长度 L≤RC,输出 O(L);队列存整条路径的做法可能产生远多于 O(RC) 的复制字符/坐标。
易错点与追问
容易失分的边界
- 只从第一岛某一个格开始 BFS 会把岛内距离算进去,可能错过从岛上其他边界出发的更短桥;应把整岛作为距离 0 源。
- 先把第二岛的陆地加入水格前驱并计成本会多翻一格;进入第二岛时即可停止。
- 题目要返回路径,只返回最少翻格数不完整;用 predecessor map 是对其空间追问的直接回应。
继续深挖
- 若有多于两座岛并要求任意两岛最短桥,可给每岛编号后做多源 BFS 并检测不同岛扩展波前相遇;需防止同一层重复计数。
- 若水格翻转成本不同,普通 BFS 失效,可在格图上使用 0-1 BFS 或 Dijkstra 并保留前驱。
