第13章封面

终于到图(Graph)了——最通用的数据结构:城市路网 🗺️、社交关系 👥、网页链接、任务依赖,万物皆可图。前 12 章学的树、堆、哈希表、并查集,本章全部上阵。

🕸️ 图的基本词汇

图 = 顶点(vertex)集合 + 边(edge)集合,边描述顶点间的成对关系:

  • 无向图:好友关系(你我是朋友,双向);
  • 有向图(digraph):关注关系(我关注你,你未必关注我);
  • 带权图:每条边带一个代价(路程、时间、费用);
  • :一个顶点连接的边数;路径、环、连通……

🗄️ 三种存储方案

结构 空间 查「u,v 相邻吗」 遍历 u 的邻居 适合
边列表 O(m) O(m) ❌ O(m) 很少查邻接
邻接矩阵 O(n²) O(1) 🏆 O(n) 稠密图
邻接表 O(n+m) 🏆 O(deg(u)) O(deg(u)) 🏆 稀疏图(现实常态)

现实中的图几乎都是稀疏图(n² 条可能的边只存在少数几条),邻接表(每个顶点挂一个邻居链表)是默认选择。

🚶 两种遍历:潜水员 vs 涟漪

图遍历

DFS(深度优先):一条路走到黑,走不通就回头。用(递归调用栈)。副产品丰富:

  • 拓扑排序:DFS 完成时间的逆序 = DAG 的合法任务顺序(选课先修、编译依赖 📚);
  • 找环连通分量(DFS 几次就有几个分量);
  • 强连通分量(有向图里互相可达的团伙):跑两遍 DFS 的 Kosaraju 算法。

BFS(广度优先):像水波纹一圈圈扩散。用队列(第 5 章!)。副产品:无权图最短路径——第一次到达某点的层数就是最短跳数。面试名场面:「社交网络里我和某明星的最短关系链」= BFS ⭐。

两者时间都是 O(n + m):每个顶点入栈/队一次,每条边扫一次——扎实又优雅。

🗺️ 最短路径:Dijkstra 算法

边带权时,BFS 失效(跳数少 ≠ 代价小)。Dijkstra:贪心 + 优先队列(第 8 章!积木复用 🧱):

  1. 起点距离 0,其余 ∞,全部丢进优先队列;
  2. 每次取出当前距离最小的顶点 u(它已确定最优,不可能再被绕路改善——贪心的正确性来源);
  3. 用 u 松弛它的邻居:dist[v] = min(dist[v], dist[u] + w(u,v)),更新过的入队;
  4. 重复到取完。

总时间 O((n+m) log n)。⚠️ 限制:边权不能为负(贪心前提崩塌),负权图要用 Bellman-Ford。

🌉 最小生成树(MST)

用 n-1 条边把 n 个顶点连成一片,总权重最小。两大贪心算法:

  • Kruskal:边按权重从小到大排序,逐条尝试加入,用并查集(第 11 章!)判环——不成环就要,成环跳过;
  • Prim:从一点出发,像滚雪球一样,每次把「离树最近的顶点」吸进来——又是优先队列的主场。

两个都是 O(m log n) 级别,一个爱稀疏图一个爱稠密图。光缆铺设、电网规划、聚类分析,全是 MST 的地盘 🌐。

🎯 本章通关清单

  • [ ] 三种图存储结构的空间与查询代价对比
  • [ ] 说出 DFS 用栈 / BFS 用队列,各举两个应用
  • [ ] 手工对一个小图跑 Dijkstra,解释「为什么取出时就是最优」
  • [ ] Kruskal 和 Prim 分别用了前面哪两章的数据结构?

到此,课内武器库全部集齐 🎓。最后一章面向真实硬件:当数据大到内存装不下怎么办?——B-Tree 登场 💾