Minimum Spanning Tree
最小生成树
最小生成树(Minimum Spanning Tree, MST)讨论的是:在一个带权无向连通图中,既要让所有顶点连通,又要让选中边的总代价尽可能低。
什么是最小生成树
设带权无向连通图为 $G=(V,E)$,$R$ 为 $G$ 的所有生成树的集合。对任意生成树 $T$,它的权值是 $T$ 中所有边权值之和:
$$
w(T)=\sum_{e\in T}w(e)
$$
若某棵生成树 $T$ 满足:
$$
w(T)=\min_{T_i\in R}w(T_i)
$$
则 $T$ 称为 $G$ 的最小生成树,也称最小代价树。
最小生成树通常只讨论带权无向连通图。
“带权”决定有代价可比较;“无向”决定边没有方向限制;“连通”决定存在覆盖全部顶点的一棵生成树。
| 条件 | 说明 |
|---|---|
| 覆盖全部顶点 | 不能只连接一部分顶点 |
| 连通 | 任意两个顶点之间都有路径 |
| 无环 | 有环就能删去环上的某条边并仍保持连通,不是生成树的最简形态 |
| 边数为 $n-1$ | 这是生成树的固定边数 |
| 边权和最小 | 在所有生成树中总代价最低 |
这些条件中,前四个保证它是生成树,最后一个保证它是最小生成树。
MST 可能不唯一,但最小权值和唯一
最小生成树可能有多棵。例如多条边权相同,或者不同生成树恰好得到同一个最小总代价时,就可能出现多棵 MST。
但要注意:
- MST 的形状和边集可能不唯一。
- MST 的最小权值和是唯一的,因为“最小值”本身是确定的。
若一个连通无向图中所有边权都相同,则每棵生成树都有 $n-1$ 条边,总代价相同。此时所有生成树都是最小生成树。
原图本身就是树
若一个带权无向连通图本身已经是一棵树,那么它只有 $n-1$ 条边,已经没有多余边可删,也没有其他生成树可选。
所以它的最小生成树就是它本身。
原图不连通
非连通图没有覆盖全图的一棵生成树,因此也没有覆盖全图的最小生成树。
此时只能在每个连通分量内部分别求最小生成树,合起来得到最小生成森林。
和 BFS/DFS 生成树的区别
广度优先生成树和深度优先生成树是由遍历过程得到的生成树。它们强调“按什么顺序访问顶点”,不保证边权和最小。
最小生成树强调“总代价最小”。它不关心 BFS 的层次,也不关心 DFS 的深入顺序,而是要在所有生成树中比较权值和。
| 生成结构 | 来源 | 是否考虑权值最小 |
|---|---|---|
| BFS 生成树 | BFS 第一次发现顶点的边 | 不保证 |
| DFS 生成树 | DFS 第一次递归进入顶点的边 | 不保证 |
| MST | 所有生成树中权值和最小者 | 保证 |
Kruskal 算法
Kruskal 的思路是“按边扩张”:把所有边按权值从小到大排序,每次选择当前最短且不会形成回路的边。
核心过程:
- 将所有边按权值从小到大排序。
- 从小到大枚举边 $(u,v,w)$。
- 若
u和v已在同一连通块中,加入这条边会成环,跳过。 - 若
u和v不在同一连通块中,选择这条边,并合并两个连通块。 - 选够 $n-1$ 条边后停止。
Kruskal 常用并查集判断是否会成环。
1 |
|
时间复杂度主要来自边排序:
$$
O(\lvert E\rvert\log\lvert E\rvert)
$$
按边表实现时,需要先把边按权值排序,然后最多检查每条边一次。检查一条边时,要判断两个端点是否已经属于同一连通块;常见写法使用并查集完成这个判断与合并。
Kruskal 适合边较少的稀疏图。
Prim 算法
Prim 的思路是“按点扩张”:从一个起点出发,维护当前生成树到每个外部顶点的最小边权,每次把距离当前树最近的顶点加入树。
核心过程:
- 任取一个顶点加入生成树集合。
- 对每个未加入顶点,记录它到当前树集合的最小边权
lowCost[v]。 - 选择
lowCost最小的未加入顶点v,把它加入生成树。 - 用
v的邻接边更新其他未加入顶点的lowCost。 - 重复直到所有顶点都加入。
朴素 Prim 常用两个数组表达这个过程:
| 数组 | 含义 | 更新时机 |
|---|---|---|
isJoin[v] 或 inTree[v] |
顶点 v 是否已经加入当前生成树集合 |
每轮选出新的最近顶点后置为 true |
lowCost[v] |
顶点 v 到当前生成树集合的最小接入代价 |
新顶点加入后,用它的邻接边尝试更新树外顶点 |
每一轮有两个扫描动作:
- 扫描所有顶点,找到
lowCost最小且尚未加入树的顶点。 - 再扫描所有顶点,用新加入顶点的邻接边更新树外顶点的
lowCost。
邻接矩阵版本适合稠密图。
1 |
|
邻接矩阵朴素 Prim 的时间复杂度为:
$$
O(\lvert V\rvert^2)
$$
因为总共进行 $\lvert V\rvert-1$ 轮,每轮大约两次线性扫描,所以总时间复杂度写作 $O(\lvert V\rvert^2)$。Prim 适合稠密图。
Kruskal 与 Prim 对比
| 算法 | 扩张对象 | 关键结构 | 常见实现 | 适合图 |
|---|---|---|---|---|
| Kruskal | 边 | 并查集 | 边集数组排序 | 稀疏图 |
| Prim | 点 | lowCost 数组 |
邻接矩阵或优先队列 | 稠密图 |
两者都基于贪心思想:每一步选一条当前看来最安全的边。差别在于 Kruskal 从全局最小边开始筛选,Prim 从一个已连通顶点集合向外扩张。
当图中存在多条等权边时,同一带权图用 Prim 和 Kruskal 可能得到边集不同的 MST;即使都使用 Kruskal,不同的等权边排序也可能得到不同的 MST。但只要都是最小生成树,最终权值和相同。
考试速记
- MST 只在带权无向连通图中讨论。
- MST 是生成树,所以一定有 $n-1$ 条边。
- MST 可能有多个,但最小权值和唯一。
- 删去 MST 任意一条边会不连通;加入原图中任意一条额外边会形成回路。
- 若原图本身是树,则 MST 就是原图本身。
- 非连通图没有生成树,只能求生成森林;带权非连通图对应最小生成森林。
- Prim 按顶点扩张,Kruskal 按边筛选。