什么是最小生成树?
生成树是相对图来说的,一个图的生成树是一个树并把图的所有顶点连接在一起。一个图可以有许多不同的生成树。一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树其实是最小权重生成树的简称。生成树的权重是考虑到了生成树的每条边的权重的总和。
最小生成树有几条边?
最小生成树有(V – 1)条边,其中V是给定的图的顶点数量。
Kruskal算法
下面是步骤寻找MST使用Kruskal算法:
1,按照所有边的权重排序(从小到大)
2,选择最小的边。检查它是否形成与当前生成树形成环。如果没有形成环,讲这条边加入生成树。否则,丢弃它。
3,重复第2步,直到有生成树(V-1)条边
具体实例及实现代码如下所示:
/** * @Title: Kruskal.java * @Package greedyalgorithm * @Description: TODO * @author peidong * @date 2017-6-1 上午9:34:10 * @version V1.0 */ package greedyalgorithm; import java.util.Collections; import java.util.LinkedList; /** * @ClassName: Kruskal * @Description: 最小生成树Kruskal算法实现 * @date 2017-6-1 上午9:34:10 * */ public class Kruskal { /** * * @ClassName: FastUnionFind * @Description: 由连通分支组成的集合为U,包括union(a,b);和find(v)的基本运算 * @date 2017-6-1 上午9:50:11 * */ public static class FastUnionFind { public int[] u; //数组用来保存顶点所属的集合,用数字表示 public FastUnionFind(int n){ u = new int[n + 1]; for(int i = 1; i <= n; i++){ //初始化顶点数组值 u[i] = i; } } public int find(int x){ //找到顶点所属的集合 return u[x]; } public void union(int x, int y){ //将第二个顶点归入第一个顶点集合 u[y] = u[x]; } } public static class EdgeNode implements Comparable{ float weight; //边权值 int u, v; //边的左右顶点 /** * * <p>Title: </p> * <p>Description:构造函数 </p> * @param u * @param v * @param weight */ public EdgeNode(int u, int v, float weight){ this.u = u; this.v = v; this.weight = weight; } /** * 按照边的权值升序排列 */ public int compareTo(Object x){ float xw = ((EdgeNode)x).weight; if(weight < xw) return -1; if(weight == xw) return 0; return 1; } } /** * * @Title: kruskal * @Description: kruskal算法 * @param n 所有顶点的数目 * @param E 边的集合(所有的边) * @param t 保存逐步连通的边 * @return 是否生成了最小生成树 * @return boolean * @throws */ public static boolean kruskal(int n, LinkedList<EdgeNode> E, EdgeNode[] t){ FastUnionFind U = new FastUnionFind(n); int k = 0; while(k < n-1){ //n个顶点,n-1条边 EdgeNode x = E.peek(); int a = U.find(x.u); //边的左顶点所属的集合 int b = U.find(x.v); //边的右顶点所属集合 if(a != b){ t[k++] = x; U.union(a, b); } E.pop(); } for(int i = 0; i < k; i++){ System.out.println("左顶点:"+t[i].u+"; 右顶点:"+t[i].v+"; 长度:"+t[i].weight); } return (k == n-1); } /** * @Title: main * @Description: TODO * @param args * @return void * @throws */ public static void main(String[] args) { // TODO Auto-generated method stub int n=6; EdgeNode e1=new EdgeNode(1,2,6); EdgeNode e2=new EdgeNode(1,3,1); EdgeNode e3=new EdgeNode(1,4,5); EdgeNode e4=new EdgeNode(2,3,5); EdgeNode e5=new EdgeNode(3,4,5); EdgeNode e6=new EdgeNode(2,5,3); EdgeNode e7=new EdgeNode(3,5,6); EdgeNode e8=new EdgeNode(5,6,6); EdgeNode e9=new EdgeNode(3,6,4); EdgeNode e10=new EdgeNode(4,6,2); LinkedList<EdgeNode> E=new LinkedList<EdgeNode>(); E.add(e10); E.add(e9); E.add(e8); E.add(e7); E.add(e6); E.add(e5); E.add(e4); E.add(e3); E.add(e2); E.add(e1); Collections.sort(E); EdgeNode[] t=new EdgeNode[n]; kruskal(n,E,t); } } //时间复杂度: // //O(ElogE) 或 O(ElogV)。 排序使用 O(ELogE) 的时间,之后我们遍历中使用并查集O(LogV) ,所以总共复杂度是 O(ELogE + ELogV)。E的值最多为V^2,所以 // //O(LogV) 和 O(LogE) 可以看做是一样的。