矩阵只是一个二维数组,但在面试中它通常代表一个网格 - 地图、棋盘或迷宫。每个单元格grid[r][c]是一个节点,它的邻居是相邻单元格。
grid = [
[1, 1, 0],
[1, 0, 0],
[0, 0, 1]
]
行r,列c,尺寸m × n。简单 - 但构建在其之上的模式非常强大。
不要编写四个单独的 if 语句,而是使用 方向数组:
# 4-directional (up, down, left, right)
dirs = [(-1,0), (1,0), (0,-1), (0,1)]
# 8-directional (includes diagonals)
dirs = [(-1,0),(1,0),(0,-1),(0,1),
(-1,-1),(-1,1),(1,-1),(1,1)]
for dr, dc in dirs:
nr, nc = r + dr, c + dc
if 0 <= nr < m and 0 <= nc < n:
# process neighbour
这是网格问题中最可重用的片段。
方向数组技巧有时称为“增量编码”。它出现在游戏开发、图像处理和竞技编程中——任何使用网格的地方。
BFS 在未加权网格中找到最短路径。从源开始,探索距离 1 处的所有邻居,然后探索距离 2 处的所有邻居,依此类推。
from collections import deque
def shortest_path(grid, start, end):
m, n = len(grid), len(grid[0])
q = deque([(start[0], start[1], 0)])
visited = {(start[0], start[1])}
dirs = [(-1,0),(1,0),(0,-1),(0,1)]
while q:
r, c, dist = q.popleft()
if (r, c) == end:
return dist
for dr, dc in dirs:
nr, nc = r+dr, c+dc
if 0<=nr<m and 0<=nc<n and (nr,nc) not in visited and grid[nr][nc]==0:
visited.add((nr, nc))
q.append((nr, nc, dist+1))
return -1
洪水填充(如油漆桶工具):从起始单元格开始,更改所有连接的相同颜色的单元格。 DFS 或 BFS 都可以。
岛屿数量: 扫描网格;当您找到 1 时,运行 DFS/BFS 将整个岛屿标记为已访问,然后增加计数。
def num_islands(grid):
count = 0
for r in range(len(grid)):
for c in range(len(grid[0])):
if grid[r][c] == '1':
dfs(grid, r, c) # mark island
count += 1
return count
在”岛屿数量”中,为什么我们将单元格标记为 DFS 期间访问过的单元格?
登录 参与讨论
所有最初腐烂的橙子都是来源。在时间0将它们全部推入队列,然后进行BFS。每个级别一分钟。当队列清空时,检查是否还有新鲜的橙子。
Minute 0: [2, 1, 1] 2 = rotten, 1 = fresh
[1, 1, 0]
[0, 1, 1]
Minute 4: [2, 2, 2] All reachable oranges rotten
[2, 2, 0]
[0, 2, 2]
关键见解:多源 BFS 同时从多个节点启动,而不是从一个节点启动。
”Rotting Oranges”与标准 BFS 有何不同?
按螺旋顺序遍历矩阵:右→下→左→上,每次通过后缩小边界。
def spiral(matrix):
res = []
top, bot = 0, len(matrix) - 1
left, right = 0, len(matrix[0]) - 1
while top <= bot and left <= right:
for c in range(left, right + 1):
res.append(matrix[top][c])
top += 1
for r in range(top, bot + 1):
res.append(matrix[r][right])
right -= 1
if top <= bot:
for c in range(right, left - 1, -1):
res.append(matrix[bot][c])
bot -= 1
if left <= right:
for r in range(bot, top - 1, -1):
res.append(matrix[r][left])
left += 1
return res
原地顺时针旋转 N×N 矩阵:转置(将 [r][c] 与 [c][r] 交换),然后 反转每一行。
Original → Transpose → Reverse rows
1 2 3 1 4 7 7 4 1
4 5 6 → 2 5 8 → 8 5 2
7 8 9 3 6 9 9 6 3
要逆时针旋转,是先反转行然后转置,还是转置然后反转列?在纸上尝试一下。
两种常见的变体:
**唯一路径:**计算从左上角到右下角仅向右或向下移动的路径。 dp[r][c] = dp[r-1][c] + dp[r][c-1]。
**最小路径总和:**相同的想法,但选择最小传入路径。
这些通常是对 2D 动态规划最温和的介绍。
在唯一路径问题中,任意列 c 的 dp[0][c] 是多少?
|图案|技术|关键问题| |--------|------------|-------------| |最短路径(未加权)| BFS |迷宫最短路径| |连接组件| DFS / BFS |岛屿数量 | |多源传播|多源BFS |腐烂的橙子| |遍历顺序 |边界指针|螺旋矩阵| |就地变换 |转置 + 反转 |旋转图像 | |计算路径 | DP |独特的路径| |在排序网格中搜索 |楼梯/二分查找 |搜索二维矩阵 |
许多网格问题实际上是变相的图形问题。在网格上什么时候你更喜欢 DFS 而不是 BFS,反之亦然?