第九章:图论算法
目标: 攻克图论这一特定领域,形成解题知识体系。
图论是算法竞赛的重镇,也是许多名校机试的压轴题来源。它研究的是由节点(顶点)和连接节点的边构成的“图”结构,能够对现实世界中纷繁复杂的关系进行建模,如社交网络、交通路网、任务依赖关系等。
许多同学一提到图论,就想到C++中复杂的邻接表(链式前向星)、指针满天飞的实现,望而生畏。但在Python的世界里,这一切都将变得无比自然和简洁。本章将带你领略,如何用Pythonic的思维和工具,优雅地解决图论问题,将C++选手还在吭哧吭哧写模板的时间,化为我们思考解题策略的优势。
9.1 图的Pythonic表示:邻接表(字典/列表)与邻接矩阵
在图论中,选择合适的存储方式是解题的第一步,这直接影响到后续算法的效率和实现难度。
1. 邻接矩阵 (Adjacency Matrix)
邻接矩阵使用一个二维数组 graph[u][v] 来存储图中顶点 u 和 v 之间的关系。如果 u 和 v 之间有边,则 graph[u][v] 的值为边的权重(对于无权图,通常记为1),否则为0或一个特殊值(如无穷大)。
- 优点: 实现简单,判断两点间是否有边非常快,时间复杂度为 O(1)O(1)O(1)。
- 缺点: 空间复杂度高达 O(V2)O(V^2)O(V2),其中 V 是顶点数量。当图中顶点很多但边很稀疏时(即“稀疏图”),会造成巨大的空间浪费。在分秒必争的竞赛中,这可能直接导致内存超限(MLE)。
Python实现:
Python
# 适用于顶点数V较小(如V <= 500)的稠密图
V = 5 # 顶点数
# 初始化一个V x V的矩阵,用inf表示点之间不直接相连
inf = float('inf')
adj_matrix = [[inf] * V for _ in range(V)]
# 添加边,例如从顶点0到顶点1,权重为10(无向图)
u, v, weight = 0, 1, 10
adj_matrix[u][v] = weight
adj_matrix[v][u] = weight # 如果是无向图,对称位置也要赋值
# 判断边是否存在
if adj_matrix[0][1] != inf:
print("边 (0, 1) 存在,权重为:", adj_matrix[0][1])
竞赛建议: 仅在顶点数量非常小,且边非常密集(接近完全图)的场景下使用,最典型的应用是Floyd算法。
2. 邻接表 (Adjacency List)
邻接表是图论算法中最常用的存储方式。它只存储实际存在的边,因此空间效率远超邻接矩阵。对于每个顶点,我们用一个列表(或其它容器)来存储所有从它出发的边所指向的顶点。
Pythonic实现方式:
在Python中,我们有比C++的 vector<vector<int>> 更优雅的选择:字典(dict)或 defaultdict。
defaultdict(list): 这是表示稀疏图的最优方式。它天生可以处理不连续的、任意标识的顶点(如字符串名字),并且在添加边时无需预先检查顶点是否存在于字典中。
Python
import collections
# 假设有E条边,V个顶点
# 顶点编号可以是0, 1, 2... 也可以是 "北京", "上海"...
graph = collections.defaultdict(list)
# 添加边 (u, v, weight)
# 场景:有向有权图
edges = [(0, 1, 10), (0, 2, 5), (1, 2, 2), (2, 3, 8)]
for u, v, w in edges:
graph[u].append((v, w)) # 存储 (邻接点, 权重)
# 场景:无向无权图
edges_unweighted = [('A', 'B'), ('A', 'C'), ('B', 'C')]
graph_unweighted = collections.defaultdict(list)
for u, v in edges_unweighted:
graph_unweighted[u].append(v)
graph_unweighted[v].append(u) # 无向图需要添加反向边
# 遍历顶点A的所有邻居
# print(graph_unweighted['A']) # 输出: ['B', 'C']
- 列表嵌套列表 (
listoflist):如果顶点编号是从0到V-1的连续整数,这种方式也是可行的,它在性能上与defaultdict相当。
Python
V = 4 # 顶点数必须已知且连续
graph_list = [[] for _ in range(V)]
edges = [(0, 1, 10), (0, 2, 5), (1, 2, 2), (2, 3, 8)]
for u, v, w in edges:
graph_list[u].append((v, w))
对比与总结:
| 特性 | 邻接矩阵 (list of list) |
邻接表 (defaultdict(list)) |
|---|---|---|
| 空间复杂度 | O(V2)O(V^2)O(V2) | O(V+E)O(V+E)O(V+E |

最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



