图不是新的内置类型,是 「点的邻居名册」。Python 里默认用哈希表 + 动态数组搭 邻接表(adjacency list);点少边密再考虑 邻接矩阵(adjacency matrix)。
段末注释:图 = 点集 + 边集。有向 / 无向、带权 / 不带权改的是边怎么解释,不是第三种存法。遍历是算法,挂在图上。

1. 两种存法
| 邻接表 | 邻接矩阵 | |
|---|---|---|
| Python 形状 | dict[node, list] 或 dict[node, dict] |
list[list] 或 方阵 |
| 空间 | (O(n+m)) | (O(n^2)) |
| 枚举邻居 | (O(\text{度数})) | (O(n)) |
| 判边 (u\to v) | (O(\text{度数}));权表可 (O(1)) | (O(1)) |
| 默认场景 | 几乎所有业务图、算法题 | (n) 很小且边很密、或要快速判边 |
(n) 点数,(m) 边数。现实图几乎都稀疏,先写邻接表。
2. 建图
1 | from collections import defaultdict |
点 ident 用可哈希对象:str、int、tuple。不要用 list 当点。
矩阵用 [[0]*n]*n 会共享行引用,必须用推导式分行创建(数组专篇踩坑第 1 条)。
3. 遍历入口:宽搜与深搜
1 | from collections import deque |
| 算法 | 结构 | 典型用途 |
|---|---|---|
| 广度优先搜索(Breadth-First Search,BFS) | deque |
无权最短路、分层 |
| 深度优先搜索(Depth-First Search,DFS) | 栈或递归 | 连通块、拓扑、环检测 |
| Dijkstra | 堆 + 距离表 | 非负权最短路 |
| Union-Find | 并查集 | 无向连通、Kruskal |
无权最短路用 BFS,不要 Dijkstra。负权才考虑 Bellman-Ford,标准库没有现成函数。
4. 有向图专题(最小集)
- 入度表:
indeg[v] += 1,Kahn 拓扑:入度为 0 的进队列。 - 环:有向图 DFS 三色(未访 / 栈中 / 已完)或拓扑后还剩点。
- 反向图:扫边
(u,v)建g[v].append(u),强连通、回溯路径用。
这些是算法,图的存储仍然是邻接表。
5. 踩坑
- 无向边只写一次:遍历会丢反向。建图就写对称,或约定「只存
u < v」并在遍历时补。 visited放晚了:宽搜在出队时才标记,队列会爆。入队即标。- 递归 DFS 深图:默认递归上限 1000。深链改显式栈。
- 邻接
list里判边:v in g[u]是 (O(\text{度数}))。频繁判边把邻居改成set或内层dict。 - 矩阵当稀疏大图:(n=10^5) 的 (n^2) 内存直接炸。
6. 自检
- 什么时候邻接矩阵优于邻接表?
- 有向带权图用
dict[u][v] = w比dict[u] = [(v,w), …]多了哪一种 (O(1))? - 无权最短路为什么是 BFS 不是堆?
[[0]*n]*n建矩阵会怎样?
7. 社区口径与风险
内置 dict + list / deque / heapq 能覆盖教学与大部分业务图。需要画图、算法电池(最短路、匹配、中心性)时社区方案是 NetworkX;风险是纯 Python、大图慢、内存胖。性能敏感路径看 graph-tool、igraph 或自己用 CSR 压缩行存储(SciPy csr_matrix)。不要为「我有一张图」先 pip install networkx 再写三行 BFS。