|
2 | 2 |
|
3 | 3 | import com.zejian.structures.heap.IndexMinPQ; |
4 | 4 |
|
| 5 | +import java.util.ArrayList; |
5 | 6 | import java.util.List; |
| 7 | +import java.util.Observable; |
6 | 8 |
|
7 | 9 | /** |
8 | 10 | * Created by zejian on 2018/1/30. |
9 | 11 | * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创] |
10 | 12 | * 优化版的最小生成树算法 |
| 13 | + * 时间复杂度 (V*LogV+E*LogV) = E*LogV |
11 | 14 | * 利用最小索引堆 |
12 | 15 | */ |
13 | 16 | public class PrimMST<Weight extends Number & Comparable<Weight>> { |
14 | 17 |
|
15 | | - private IndexMinPQ<Weight> pq;//最小索引堆,只存储权值更小的边,不会存放所有边 |
| 18 | + private IndexMinPQ<Weight> pq;//最小索引堆,只存储权值更小的边,不会存放所有边 |
16 | 19 | private boolean marked[];//标记数组, 在算法运行过程中标记节点i是否被访问 |
17 | 20 | private Edge edgeTo[];//访问的点所对应的边, 因为索引堆中没存放具体边信息 |
18 | 21 | private List<Edge<Weight>> mst; // 最小生成树所包含的所有边 |
19 | | - private Number mWeight; //最小生成树的总权值 |
| 22 | + private Number mstWeight; //最小生成树的总权值 |
20 | 23 |
|
| 24 | + public PrimMST(WeightGraph G) { |
| 25 | + assert G != null; |
| 26 | + pq = new IndexMinPQ<Weight>(G.V()); |
| 27 | + marked = new boolean[G.V()]; |
| 28 | + edgeTo = new Edge[G.V()]; |
| 29 | + mst = new ArrayList<Edge<Weight>>(); |
| 30 | + |
| 31 | + for (int i = 0; i < G.V(); i++) { |
| 32 | + edgeTo[i] = null; |
| 33 | + } |
| 34 | + |
| 35 | + |
| 36 | + //prim算法 |
| 37 | + visited(G, 0); |
| 38 | + while (!pq.isEmpty()) { |
| 39 | + int x = pq.deleteMin(); |
| 40 | + if (edgeTo[x] != null) { |
| 41 | + mst.add(edgeTo[x]); |
| 42 | + visited(G,x); |
| 43 | + } |
| 44 | + } |
| 45 | + |
| 46 | + |
| 47 | + // 计算最小生成树的权值 |
| 48 | + mstWeight = mst.get(0).wt(); |
| 49 | + for( int i = 1 ; i < mst.size() ; i ++ ) |
| 50 | + mstWeight = mstWeight.doubleValue() + mst.get(i).wt().doubleValue(); |
| 51 | + |
| 52 | + } |
| 53 | + |
| 54 | + private void visited(WeightGraph graph, int v) { |
| 55 | + assert !marked[v]; |
| 56 | + |
| 57 | + marked[v] = true; //设置访问标志 |
| 58 | + |
| 59 | + //遍历邻边 |
| 60 | + for (Object o : graph.adj(v)) { |
| 61 | + Edge<Weight> e = (Edge<Weight>) o; |
| 62 | + |
| 63 | + int w = e.other(v); |
| 64 | + // 如果边的另一端点已被访问,跳过该顶点 |
| 65 | + if (marked[w]) continue; |
| 66 | + // 如果从没有考虑过这个端点, 直接将这个端点和与之相连接的边加入索引堆 |
| 67 | + if (edgeTo[w] == null) { |
| 68 | + edgeTo[w] = e; |
| 69 | + pq.insert(w, e.wt()); |
| 70 | + } |
| 71 | + //如果曾经考虑这个端点, 但现在的边比之前考虑的边更短, 则进行替换 |
| 72 | + else if (edgeTo[w] != null && edgeTo[w].compareTo(e) > 0) { |
| 73 | + edgeTo[w] = e; |
| 74 | + pq.change(w, e.wt()); |
| 75 | + } |
| 76 | + |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + /** |
| 81 | + * 返回最小生成树的所有边 |
| 82 | + * @return |
| 83 | + */ |
| 84 | + public List<Edge<Weight>> getMstEdgeList(){ |
| 85 | + return mst; |
| 86 | + } |
| 87 | + |
| 88 | + /** |
| 89 | + * 返回最小生成树的权值 |
| 90 | + * @return |
| 91 | + */ |
| 92 | + public Number mstWeight(){ |
| 93 | + return mstWeight; |
| 94 | + } |
| 95 | + |
| 96 | + |
| 97 | + // 测试 Prim |
| 98 | + public static void main(String[] args) { |
| 99 | + String filename = "testWG1.txt"; |
| 100 | + WeightSparseGraph<Double> wSparseGraph = new WeightSparseGraph<Double>(8,false); |
| 101 | + wSparseGraph.readGraph(filename); |
| 102 | + wSparseGraph.show(); |
| 103 | + |
| 104 | + PrimMST<Double> primMst = new PrimMST<Double>(wSparseGraph); |
| 105 | + List<Edge<Double>> mstList = primMst.getMstEdgeList(); |
| 106 | + for( int i = 0 ; i < mstList.size() ; i ++ ) |
| 107 | + System.out.println(mstList.get(i)); |
| 108 | + System.out.println("The MST weight is: " + primMst.mstWeight()); |
| 109 | + System.out.println(); |
| 110 | + } |
21 | 111 |
|
22 | 112 | } |
0 commit comments