最小生成树
定义
在阅读下列内容之前,请务必阅读 图论相关概念 与 树基础 部分,并了解以下定义:
生成子图
生成树
我们定义无向连通图的 最小生成树(Minimum Spanning Tree,MST)为边权和最小的生成树。
注意:只有连通图才有生成树,而对于非连通图,只存在生成森林。
Kruskal 算法
Kruskal 算法是一种常见并且好写的最小生成树算法,由 Kruskal 发明。该算法的基本思想是从小到大加入边,是个贪心算法。
前置知识
实现
图示:
伪代码:

算法虽简单,但需要相应的数据结构来支持……具体来说,维护一个森林,查询两个结点是否在同一棵树中,连接两棵树。
抽象一点地说,维护一堆 集合,查询两个元素是否属于同一集合,合并两个集合。
其中,查询两点是否连通和连接两点可以使用并查集维护。
如果使用 的排序算法,并且使用
或
的并查集,就可以得到时间复杂度为
的 Kruskal 算法。
证明
思路很简单,为了造出一棵最小生成树,我们从最小边权的边开始,按边权从小到大依次加入,如果某次加边产生了环,就扔掉这条边,直到加入了 条边,即形成了一棵树。
证明:使用归纳法,证明任何时候 K 算法选择的边集都被某棵 MST 所包含。
基础:对于算法刚开始时,显然成立(最小生成树存在)。
归纳:假设某时刻成立,当前边集为 ,令
为这棵 MST,考虑下一条加入的边
。
如果 属于
,那么成立。
否则, 一定存在一个环,考虑这个环上不属于
的另一条边
(一定只有一条)。
首先, 的权值一定不会比
小,不然
会在
之前被选取。
然后, 的权值一定不会比
大,不然
就是一棵比
还优的生成树了。
所以, 包含了
,并且也是一棵最小生成树,归纳成立。
Prim 算法
Prim 算法是另一种常见并且好写的最小生成树算法。该算法的基本思想是从一个结点开始,不断加点(而不是 Kruskal 算法的加边)。
实现
图示:
具体来说,每次要选择距离最小的一个结点,以及用新的边更新其他结点的距离。
其实跟 Dijkstra 算法一样,每次找到距离最小的一个点,可以暴力找也可以用堆维护。
堆优化的方式类似 Dijkstra 的堆优化,但如果使用二叉堆等不支持 decrease-key 的堆,复杂度就不优于 Kruskal,常数也比 Kruskal 大。所以,一般情况下都使用 Kruskal 算法,在稠密图尤其是完全图上,暴力 Prim 的复杂度比 Kruskal 优,但 不一定 实际跑得更快。
暴力:
二叉堆:
Fib 堆:
伪代码:

注意:上述代码只是求出了最小生成树的权值,如果要输出方案还需要记录每个点的 代表的是哪条边。
Last updated
Was this helpful?