前言
最小生成树,对于一个无向图,联通且不含圈的图称为树。
给定无向图G=(V,E),连接G中所有点,且边集是E的子集的数称为G的生成树(Spanning Tree),而权值最小的生成树称为最小生成树(Minimal Spanning Tree, MST)。构造MST的算法有很多,最常见的有两个:Kruskal算法和Prim算法。这里只介绍Kruskal算法,因为这个算法编写容易且效率很高。
前面描述不清楚的话,可以看下图
这是一个无向图,每条边有权重,你可以采用几条边就能连接所有的点,就得到了一颗生成树(前提是不含圈)。如果树的所有边的权值之和最小,就称为最小生成树。
算法流程
Kruskal算法的流程非常简单和直接:
- 1、对于边集E,对所有的边按照权值从小到大排序
- 2、 依次将排序好的边放入MST中,如果加入这条边(u,v)使图出现了环,则放弃
- 3、直到所有的点都出现在MST中,构造完成。
其中第2步可能出现环,则需要检验环,这里可以采用DFS或者BFS的方式进行图遍历(写起来很复杂,需要判断什么样才算是环,而且复杂度很高)。所以我们采用另外一种方式来进行检验,那就是"并查集"。并查集可以查看我之前的博文并查集解释。
另外第2步会出现的情况就是已经有几个边入选但是构不成树,如下图所示:
事实上我们并不需要马上构成树,我们只需要将边选出来,然后构造点的集合就可以了,这也就是为什么要用并查集的另一个理由,例如上图中有三个集合,分别是{4,6},{2,5},{1,3,}。如果连同了,那两个集合就合并为一个集合。其他还没有边连接的点构成另外的集合,例如{7},{8},{9}…
那并查集如何判断环呢?其实只要判断边的两个端点(u,v)是否在同一个集合中,如果在同一个集合中,说明已经联通(构成树),你再添加就会构成环。
以上,kruskal算法应该解释清楚了。
C++模板
struct Edge{
int from, to, cost;
Edge(int from, int to, int cost){
this->from = from;