最小生成树
最小生成树(MST)在连通的无向加权图中,用总权重最小的一组边连接全部顶点。它不含环,V 个顶点恰好使用 V - 1 条边。
它与最短路径目标不同:最短路径计算一个源点到各处的最低成本;MST 寻找连接所有顶点的最低总成本网络。最终树中两点间的路径,不一定是原图中两点间的最短路径。
本课在同一张图上比较两种贪心算法。Prim 扩展一棵连通树,每次选边必须跨越树内外的割;Kruskal 从每个顶点一个分量开始,按权重从小到大扫描边,只接受连接不同分量的边。
两种算法如何工作
Prim 用集合 S 保存已加入树的顶点,用最小堆保存前沿边。候选边入队时一端在 S,另一端在外。弹出最轻边后,若外端仍在外,就接受并加入该顶点;若等待期间两端都已进入树,条目过期,应拒绝。
画面左侧直接显示割的两边 S 和 V - S。割性质保证:跨越该割的最轻边可以安全地加入某棵与当前选择兼容的 MST。每加入一个顶点,Prim 就在新割上再次应用这个性质。
Kruskal 先排序全部边,再用并查集判断两端是否已由接受边连通。根不同就接受并合并;根相同说明已有路径相连,新增边会形成环,必须拒绝。
两种算法都允许负权。负边只是优先级更高的候选,仍须满足连接和无环条件。这一点与要求非负权的 Dijkstra 不同。
最小生成树逐步演示
默认桥接预设有 A–F 六个顶点,无向边为 B–C (1)、B–D (2)、C–D (3)、C–E (4)、D–F (5)、D–E (6)、A–C (7)、A–B (8)、E–F (9)。
Prim 从 A 开始:
- A 加入
S,暴露A–C (7)和A–B (8)。 - 弹出
A–C (7),跨越当前割,接受并加入 C,总重 7。新增C–B (1)、C–D (3)、C–E (4)。 - 弹出并接受
C–B (1),加入 B,总重 8,新增B–D (2)。 - 接受
B–D (2),加入 D,总重 10,新增D–F (5)、D–E (6)。 - 弹出
C–D (3)。C、D 已通过C–B、B–D连通,拒绝该边以防成环。 - 接受
C–E (4),加入 E,总重 14,新增E–F (9)。 - 接受
D–F (5),加入 F,总重 19。 - 已接受
6 - 1 = 5条边,树完成,停止。此前的安全选择已构成 MST,无需继续处理前沿。
接受边为 A–C、B–C、B–D、C–E、D–F,总重 19。
Kruskal 则先接受 B–C (1)、B–D (2),因同分量而拒绝 C–D (3);再接受 C–E (4)、D–F (5);拒绝成环的 D–E (6);最后用 A–C (7) 把 A 接入,五条边总重仍为 19。
Prim 受一棵树的当前割约束,Kruskal 可以在图中任意位置连接分量,所以选边顺序不同。本图边权互异,MST 唯一,因此结果相同。
时间与空间复杂度
邻接表加二叉最小堆的 Prim,在本课简单图模型下时间为 O((V + E) log V),图与堆共占 O(V + E) 空间。懒前沿堆可达 O(E) 项,严格的堆操作为 O(log E),简单图中可等价写为 O(log V)。
Kruskal 排序为 O(E log E),按秩合并和路径压缩的并查集操作合计 O(E α(V)),实际输入范围内接近线性。边列表和并查集数组占 O(V + E)。
稠密图已有邻接矩阵时,O(V²) 的 Prim 实现可能合适;稀疏图已有边列表时,Kruskal 往往更直接。
边界情况
- 单顶点:空边集就是生成树,总重 0。
- 图不连通:不存在生成树。Prim 未覆盖全部顶点就耗尽前沿,Kruskal 扫完边仍有多个分量。不连通预设会报告失败,不把部分森林当 MST。
- 相同边权:可能存在不同但等重的 MST。本课保持同权边的输入顺序以固定回放,其他平局顺序也可能得到合法最优树。
- 负权合法:负边预设接受权重
-3的边,最终总重 3。 - 平行边:分别作为候选,轻边连接两端后,重边会成环。
- 自环:不能连接不同分量,应忽略。
常见错误
- Prim 选择全图最轻剩余边。两端都在树外的便宜边还不能扩展当前树,只能从当前割选。
- Kruskal 只看端点是否出现过。要比较两端的并查集根;出现过的端点仍可连接不同分量。
- 每个顶点都出现在某条接受边中就停止。多个分量也能覆盖全部顶点;在无环条件下,连通图接受
V - 1条边才完成。 - 用在有向图上。有向对应问题通常称为最小树形图,需要不同算法。
- 误以为负权使 Prim、Kruskal 失效。非负限制属于 Dijkstra。
- 将不连通图的部分森林报告成生成树。最小生成森林有用途,但应按实际结果命名。
动画中的展示方式
Prim 的前沿画成完全排序行,真实二叉堆只有部分有序,保证能取最小值。Kruskal 的整行确实排序,因为全边排序就是算法的一步。
代码面板展示核心流程,假设图、堆、并查集已经实现。动画将堆插入和分量合并拆成独立节拍,便于检查每次状态变化。