|
| 1 | +package com.zejian.structures.Graph.WeightGraph; |
| 2 | + |
| 3 | +import com.zejian.structures.heap.MinHeap; |
| 4 | + |
| 5 | +import java.util.ArrayList; |
| 6 | +import java.util.List; |
| 7 | + |
| 8 | +/** |
| 9 | + * Created by zejian on 2018/1/30. |
| 10 | + * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] |
| 11 | + * 利用Kruskal算法求最小生成树(只适合无向图) |
| 12 | + * 算法思想:按照边的权重排序(最小堆数据结构),每次将最小权重边加入最小生成树(List数据结构存储就行) |
| 13 | + * 但在加入前要判断加入的边是否会与已存在的生成树中的边形成环,如果形成环,那么就丢弃该边,不加入最小 |
| 14 | + * 生成树,以次循环直到最小生成树含有V-1条边,那么就结束操作,此时List中存储的边就是一棵最小生成树. |
| 15 | + * |
| 16 | + * 需要的工具:最小堆数据结构(排序所有边)+List(存储最小生成树)+并查集UF(用于判断是否形成环) |
| 17 | + */ |
| 18 | +public class KruskalMST<Weight extends Number & Comparable<Weight>> { |
| 19 | + |
| 20 | + private List<Edge<Weight>> mst;//最小生成树所包含的所有边 |
| 21 | + private Number mstWeight; //最小生成树的总权值 |
| 22 | + private int count = 0; |
| 23 | + public KruskalMST(WeightGraph graph){ |
| 24 | + assert graph != null; |
| 25 | + mst = new ArrayList<Edge<Weight>>(); |
| 26 | + |
| 27 | + MinHeap<Edge<Weight>> pq = new MinHeap<Edge<Weight>>(graph.E()); |
| 28 | + //遍历所有边并加入pq |
| 29 | + for (int i = 0; i <graph.V() ; i++) { |
| 30 | + for (Object item: graph.adj(i)) { |
| 31 | + if(item != null) { |
| 32 | + Edge<Weight> e = (Edge<Weight>) item; |
| 33 | + //因为是无向图,存在重复边.所以这里需要判断一下如(0,1)和(1,0)是同一条边 |
| 34 | + if (e.v() < e.w()) { |
| 35 | + count ++; |
| 36 | + System.out.println("count:"+count+",e="+e.toString()); |
| 37 | + pq.insert(e); |
| 38 | + } |
| 39 | + } |
| 40 | + } |
| 41 | + } |
| 42 | + //创建并查集辅助类,用于判断是否存在环 |
| 43 | + UnionFind uf = new UnionFind(graph.V()); |
| 44 | + |
| 45 | + //最小生成树含有V-1条边时停止 |
| 46 | + while (!pq.isEmpty()&& mst.size() < graph.V() - 1 ){ |
| 47 | + //取出最小权值值边 |
| 48 | + Edge<Weight> e = pq.deleteMin(); |
| 49 | + |
| 50 | + int v = e.either(); |
| 51 | + int w = e.other(v); |
| 52 | + //判断要相连的两个顶点是否有相同根结点,没有就可以加入生成树 |
| 53 | + //如果有那么说明相连后肯定形成环,抛弃该边 |
| 54 | + if(uf.isConnected(v,w)){ |
| 55 | + continue; |
| 56 | + } |
| 57 | + |
| 58 | + mst.add(e); |
| 59 | + //把已访问过的最小生成树的边结点加入并查集 |
| 60 | + uf.unionElements(v,w); |
| 61 | + } |
| 62 | + |
| 63 | + //计算最小生成树的总权值 |
| 64 | + mstWeight = mst.get(0).wt().doubleValue(); |
| 65 | + for (int i = 1; i < mst.size() ; i++) { |
| 66 | + mstWeight =mstWeight.doubleValue() + mst.get(0).wt().doubleValue(); |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + // 返回最小生成树的所有边 |
| 71 | + List<Edge<Weight>> getMstEdgeList(){ |
| 72 | + return mst; |
| 73 | + } |
| 74 | + |
| 75 | + // 返回最小生成树的权值 |
| 76 | + Number mstWeight(){ |
| 77 | + return mstWeight; |
| 78 | + } |
| 79 | + |
| 80 | + |
| 81 | + /** |
| 82 | + * TODO:测试未通过............. |
| 83 | + * @param args |
| 84 | + */ |
| 85 | + public static void main(String[] args) { |
| 86 | + |
| 87 | + String filename = "testWG1.txt"; |
| 88 | + WeightSparseGraph<Double> spare = new WeightSparseGraph<Double>(8,false); |
| 89 | + spare.readGraph(filename); |
| 90 | + spare.show(); |
| 91 | + |
| 92 | + KruskalMST<Double> kMST = new KruskalMST<Double>(spare); |
| 93 | + List<Edge<Double>> mstList = kMST.getMstEdgeList(); |
| 94 | + for( int i = 0 ; i < mstList.size() ; i ++ ) |
| 95 | + System.out.println(mstList.get(i)); |
| 96 | + System.out.println("总权值为:"+kMST.mstWeight()); |
| 97 | + |
| 98 | + |
| 99 | + } |
| 100 | +} |
0 commit comments