图算法可视化
图算法回答不同问题:哪个依赖可以先执行?一个起点到各顶点的最小成本是多少?连接整张图的总成本如何最小?拓扑排序回答顺序问题,Dijkstra 和 Bellman-Ford 求单源最短路,最小生成树优化整体连接成本。
最短路径树保证从源点出发的路线最短,不保证选中边的总和最小。最小生成树最小化总边权,却可能让某对顶点之间的路线更长。
目标比较
| 算法 | 目标 | 下一步选择 | 主要条件 | 典型时间 |
|---|---|---|---|---|
| 拓扑排序 | 按依赖排列顶点 | Kahn 选零入度点,DFS 在返回时记录完成点 | 有向无环图 | O(V + E) |
| Dijkstra | 单源最短距离 | 暂定距离最小的未确定顶点 | 边权非负 | O((V + E) log V) |
| Bellman-Ford | 单源最短距离 | 每轮松弛每条边 | 源点可达范围没有负环 | O(V · E) |
| Prim | 最小生成树 | 当前树与外部之间的最轻边 | 无向连通图 | O((V + E) log V) |
| Kruskal | 最小生成树 | 连接不同分量的下一条最轻边 | 无向连通图 | O(E log E) |
Dijkstra 与 Prim 都可以使用最小堆,但优先值不同:前者是从源点出发的整条路径成本,后者是一条跨割边的权重。Kruskal 不围绕源点生长,而是排序所有边,用并查集根判断是否可以连接。
建议学习顺序
学过 DFS 和 BFS 后,先看拓扑排序。Kahn 从零入度点开始移除依赖,DFS 在回退时记录完成顺序。同一 DAG 可以产生两个不同的合法顺序;环形预设让两种算法都拒绝把部分结果当成完整答案。
再看 Dijkstra,跟踪暂定距离与松弛。尚未确定的路线可能被后续路径改善。负权预设展示条件边界:边能降低成本时,最小距离出堆后直接定案就不再安全。
随后在 Bellman-Ford 中选择 Dijkstra 陷阱预设,即刚才算错的同一张图。它不使用优先队列或永久锁定,而是重复松弛边,最多经过 V−1 轮得到最短路,再用额外检查判断是否存在可达负环。
进入最小生成树的 Prim 模式。左侧记录显示当前割,每次接受最轻跨割边。负权在这里不会造成 Dijkstra 的问题。
保持图不变,切换到 Kruskal。下方是全局排序的边,左侧是连通分量。若两端根相同,已接受的路径会指出候选边将闭合哪个环。
观察重点
- 分清堆里的优先值是整条路径成本,还是单条边权。
- 拓扑排序中,Kahn 用正入度说明仍有前置约束;DFS 需要先处理尚未完成的后继,再记录当前顶点。
- 最便宜的候选也必须满足算法不变量,才能安全接受。
- 观察拒绝步骤:Dijkstra 丢弃过期堆条目,生成树算法拒绝会闭环的边。
- 尝试不连通图。得到的部分森林不能当成整图的生成树。
全部图算法可视化课程
改变图或算法,逐步对照 Python 或 JavaScript 代码查看轨迹。