6.4 最小生成树

最小生成树的性质,Prim 算法与 Kruskal 算法的过程、实现要点与复杂度对比。

考查要求 ★★★ 重点掌握 MST 性质Prim 算法Kruskal 算法复杂度对比唯一性

最小生成树的概念

一个连通图的生成树包含图的所有顶点,并且只含尽可能少的边。对于生成树来说,若砍去它的一条边,则会使生成树变成非连通图;若给它增加一条边,则会形成图中的一条回路。

对于一个带权连通无向图 G=(V,E)G = (V, E),生成树不同,每棵树的权(即树中所有边上的权值之和)也可能不同。设 R\mathfrak{R} 为 GG 的所有生成树的集合,若 TT 为 R\mathfrak{R} 中边的权值之和最小的生成树,则 TT 称为 GG 的最小生成树(Minimum Spanning Tree,MST)。

构造最小生成树的算法大多利用了如下性质(MST 性质):假设 G=(V,E)G = (V, E) 是一个带权连通无向图,UU 是顶点集 VV 的一个非空子集。若 (u,v)(u, v) 是一条具有最小权值的边,其中 u∈Uu \in U,v∈V−Uv \in V - U,则必存在一棵包含边 (u,v)(u, v) 的最小生成树。

基于该性质的最小生成树算法主要有 Prim 算法和 Kruskal 算法,它们都基于贪心算法的策略。

Prim 算法

Prim(普里姆)算法的执行非常类似于寻找图的最短路径的 Dijkstra 算法。

  1. 初始时从图中任取一顶点(如顶点 1)加入树 T,此时树中只含有一个顶点;
  2. 之后选择一个与当前 T 中顶点集合距离最近的顶点,并将该顶点和相应的边加入 T,每次操作后 T 中的顶点数和边数都增 1;
  3. 以此类推,直至图中所有的顶点都并入 T,得到的 T 就是最小生成树。此时 T 中必然有 n−1n - 1 条边。
动画演示Prim 算法
6155536426v1v2v3v4v5v6lowcost[]:各顶点到 U 的最小边权(✓ 表示已在 U 中)v1✓v26v31v45v5∞v6∞
候选边本步选中生成树的边
算法描述
1void Prim(G, T) {
2 T = ∅; // 初始化空树
3 U = {w}; // 添加任意一个顶点 w
4 while ((V - U) != ∅) { // 若树中不含全部顶点
5 设 (u, v) 是使 u ∈ U 与 v ∈ (V - U),
6 且权值最小的边;
7 T = T ∪ {(u, v)}; // 边归入树
8 U = U ∪ {v}; // 顶点归入树
9 更新 V - U 中各顶点到 U 的最小代价 lowcost[];
10 }
11}
1/21从顶点 v1 开始,U = {v1}。lowcost[] 记录 V−U 中各顶点到 U 的最小边权
U 中顶点数1WPL/权值和0

实现时用两个辅助数组:lowcost[] 记录 V−UV - U 中各顶点到 UU 的最小边权,adjvex[](或 closest[])记录对应边在 UU 中的端点。每并入一个新顶点 kk,都用 kk 到其余顶点的边权去更新 lowcost[]。

时间复杂度 O(∣V∣2)O(|V|^2),不依赖于 ∣E∣|E|,因此它适用于求解边稠密的图的最小生成树。虽然采用其他方法能改进 Prim 算法的时间复杂度,但增加了实现的复杂性。

Kruskal 算法

与 Prim 算法从顶点开始扩展最小生成树不同,Kruskal(克鲁斯卡尔)算法是一种按权值的递增次序选择合适的边来构造最小生成树的方法。

  1. 初始时为只有 nn 个顶点而无边的非连通图 T=(V,{})T = (V, \{\}),每个顶点自成一个连通分量;
  2. 然后按照边的权值由小到大的顺序,不断选取当前未被选取过且权值最小的边,若该边依附的顶点落在 T 中不同的连通分量上,则将此边加入 T,否则舍弃此边而选择下一条权值最小的边;
  3. 以此类推,直至 T 中所有顶点都在一个连通分量上。
动画演示Kruskal 算法
6155536426v1分量 1v2分量 2v3分量 3v4分量 4v5分量 5v6分量 6按权值排序后的边v1v31v4v62v2v53v3v64v1v45v2v35v3v45v1v26v3v56v5v66
候选边本步选中生成树的边形成回路,舍弃
算法描述
1void Kruskal(V, T) {
2 T = V; // 初始化树 T,仅含顶点
3 numS = n; // 连通分量数
4 while (numS > 1) { // 若连通分量数大于 1
5 从 E 中取出权值最小的边 (v, u);
6 if (v 和 u 属于 T 中不同的连通分量) {
7 T = T ∪ {(v, u)}; // 将此边加入生成树中
8 numS--; // 连通分量数减 1
9 } // 否则舍弃(会形成回路)
10 }
11}
1/14初始时只有 6 个顶点而无边,每个顶点自成一个连通分量。将边按权值递增排序
连通分量数6权值和0

在 Kruskal 算法中,最坏情况需要对 ∣E∣|E| 条边各扫描一次。通常在 Kruskal 算法中,采用堆来存放边的集合,因此每次选择最小权值的边只需 O(log⁡2∣E∣)O(\log_2|E|) 的时间。此外,由于生成树 T 中的所有边可视为一个等价类,因此每次添加新的边的过程类似于求解等价类的过程,由此可以采用并查集来描述 T,从而构造 T 的时间复杂度为 O(∣E∣log⁡2∣E∣)O(|E| \log_2 |E|)。因此,Kruskal 算法适合于边稀疏而顶点较多的图。

两种算法的对比

Prim 算法Kruskal 算法
思想加点法:从一个顶点开始,每次加入距离 U 最近的顶点加边法:按权值递增依次加入不形成回路的边
时间复杂度O(∣V∣2)O(\lvert V\rvert ^2)O(∣E∣log⁡2∣E∣)O(\lvert E\rvert \log_2 \lvert E\rvert )
适用边稠密图边稀疏图
中间过程始终是一棵树可能是森林,最后才连成一棵树

即时练习

即时练习4 题
点击选项即可作答
  1. 1
    下列关于最小生成树的叙述中,正确的是( )。
    Ⅰ. 最小生成树的代价唯一
    Ⅱ. 所有权值最小的边一定会出现在所有的最小生成树中
    Ⅲ. 使用 Prim 算法从不同顶点开始得到的最小生成树一定相同
    Ⅳ. 使用 Prim 算法和 Kruskal 算法得到的最小生成树总不相同
  2. 2
    用 Prim 算法求本课示例图(从 v1 出发)的最小生成树,第 2 条加入的边是( )。
  3. 3
    用 Kruskal 算法求本课示例图的最小生成树,依次加入的前三条边的权值是( )。
  4. 4
    对于有 n 个顶点、e 条边的带权连通图,Prim 算法与 Kruskal 算法(借助堆和并查集)的时间复杂度分别为( )。
学完了吗?

标记完成后,侧栏会显示进度。进度仅保存在本浏览器中。