图是最强大的数据结构,社交网络、地图导航、网页链接皆由图来表达
图(Graph)由顶点(Vertex)和边(Edge)组成,是表达实体间关系最自然的数据结构。树和链表都是图的特例。面试中,图的表示、遍历、连通性是最核心的考点。
# 邻接表表示(面试推荐)
from collections import defaultdict
# 无向图
graph = defaultdict(list)
graph[0].append(1) # 添加边 0-1
graph[1].append(0) # 无向图需要双向添加
# 有向图
digraph = defaultdict(list)
digraph[0].append(1) # 只添加 0→1
# 邻接矩阵(稠密图时使用)
n = 5
matrix = [[0] * n for _ in range(n)]
matrix[0][1] = 1 # 添加边 0→1
# 加权图
weighted = defaultdict(list)
weighted[0].append((1, 5)) # 边 0→1, 权重5
defaultdict(list) 是最简洁的写法。题目描述:给定无向连通图中一个节点的引用,返回该图的深拷贝。图中的每个节点都包含它的值 val 和其邻居的列表 neighbors。
visited 映射原节点→副本节点,避免重复创建和死循环。class Solution:
def cloneGraph(self, node: 'Node') -> 'Node':
if not node:
return None
visited = {} # 原节点 -> 克隆节点
def dfs(n):
if n in visited:
return visited[n]
# 创建克隆节点
copy = Node(n.val)
visited[n] = copy
# 递归克隆所有邻居
for neighbor in n.neighbors:
copy.neighbors.append(dfs(neighbor))
return copy
return dfs(node)
时间复杂度:O(V+E) — 每个节点和边访问一次
空间复杂度:O(V) — 哈希表存储所有节点映射 + 递归栈深度
visited 防止死循环!这是图和树遍历的核心区别——树没有环,图可能有。忘记 visited 是最常见的 bug。题目描述:给你一个由 '1'(陆地)和 '0'(水)组成的二维网格,计算网格中岛屿的数量。岛屿是被水包围的陆地,由相邻的陆地连接而成(水平或垂直方向相邻)。
'1' 就启动 DFS,将连通的所有 '1' 标记为已访问(改为 '#' 或其他字符),然后岛屿计数 +1。class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
if not grid:
return 0
rows, cols = len(grid), len(grid[0])
count = 0
def dfs(r, c):
# 越界或不是陆地,返回
if r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] != '1':
return
# 标记已访问(沉岛)
grid[r][c] = '#'
# 向四个方向扩展
dfs(r + 1, c)
dfs(r - 1, c)
dfs(r, c + 1)
dfs(r, c - 1)
for r in range(rows):
for c in range(cols):
if grid[r][c] == '1':
dfs(r, c)
count += 1
return count
时间复杂度:O(m×n) — 每个格子最多访问一次
空间复杂度:O(m×n) — 最坏情况递归深度为 m×n
题目描述:给定一个包含 0 和 1 的二维二进制矩阵,找出矩阵中最大的岛屿面积。岛屿的面积是连通的 1 的数量。
1,面积 +1,递归累加四个方向的面积。class Solution:
def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
if not grid:
return 0
rows, cols = len(grid), len(grid[0])
max_area = 0
def dfs(r, c):
if r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] != 1:
return 0
grid[r][c] = 0 # 标记已访问
area = 1 # 当前格子
area += dfs(r + 1, c)
area += dfs(r - 1, c)
area += dfs(r, c + 1)
area += dfs(r, c - 1)
return area
for r in range(rows):
for c in range(cols):
if grid[r][c] == 1:
max_area = max(max_area, dfs(r, c))
return max_area
时间复杂度:O(m×n) — 每个格子访问一次
空间复杂度:O(m×n) — 递归深度最坏为 m×n
DFS 和 BFS 是图遍历的两大基石,它们在面试中的应用场景有所不同:
| 特性 | DFS(深度优先) | BFS(广度优先) |
|---|---|---|
| 数据结构 | 栈(递归或显式栈) | 队列 |
| 遍历顺序 | 一条路走到底 | 逐层扩展 |
| 空间复杂度 | O(V) 栈深度 | O(V) 队列大小 |
| 适合问题 | 连通性、路径搜索、回溯 | 最短路径、层序遍历 |
| 岛屿问题 | ✅ 沉岛法 | ✅ 层序扩展 |
| 最短路 | ❌ 不保证最短 | ✅ 无权图最短路 |
from collections import deque, defaultdict
def bfs_graph(graph, start):
visited = set([start])
queue = deque([start])
while queue:
node = queue.popleft()
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
return visited
def dfs_graph(graph, node, visited=None):
if visited is None:
visited = set()
visited.add(node)
for neighbor in graph[node]:
if neighbor not in visited:
dfs_graph(graph, neighbor, visited)
return visited
sys.setrecursionlimit(10**5) 提高限制;(3) 用显式栈模拟递归。defaultdict(list) 是最佳选择