《Python算法竞赛攻略:从入门到降维打击》——第九章

第九章:图论算法

目标: 攻克图论这一特定领域,形成解题知识体系。

图论是算法竞赛的重镇,也是许多名校机试的压轴题来源。它研究的是由节点(顶点)和连接节点的边构成的“图”结构,能够对现实世界中纷繁复杂的关系进行建模,如社交网络、交通路网、任务依赖关系等。

许多同学一提到图论,就想到C++中复杂的邻接表(链式前向星)、指针满天飞的实现,望而生畏。但在Python的世界里,这一切都将变得无比自然和简洁。本章将带你领略,如何用Pythonic的思维和工具,优雅地解决图论问题,将C++选手还在吭哧吭哧写模板的时间,化为我们思考解题策略的优势。


9.1 图的Pythonic表示:邻接表(字典/列表)与邻接矩阵

在图论中,选择合适的存储方式是解题的第一步,这直接影响到后续算法的效率和实现难度。

1. 邻接矩阵 (Adjacency Matrix)

邻接矩阵使用一个二维数组 graph[u][v] 来存储图中顶点 uv 之间的关系。如果 uv 之间有边,则 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']
  • 列表嵌套列表 (list of list):如果顶点编号是从 0V-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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱看烟花的码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值