package org.loda.graph; import org.loda.structure.MinQ; import org.loda.structure.Queue; import org.loda.util.In; /** * * @ClassName: PrimMST * @Description: 最小生成樹 prim算法 * * 利用貪心算法不斷訪問鏈接最小生成樹的最小的邊 * * 實際應用:給多個城市鋪設電纜,每一個城市以前鋪設的開銷不一樣,如何最省成本?能夠採用最小生成樹算法 * * @author minjun * @date 2015年5月25日 下午8:34:43 * */ public class PrimMST { // visited[i]表示i是否被訪問過 private boolean[] visited; // 最小生成樹 private Queue<Edge> mst; // 最小生成樹的權重 private double weight; // 按權重從小到大的順序存儲edge優先隊列 private MinQ<Edge> q; public PrimMST(WeightGraph g) { // 頂點的數量 int v = g.v(); mst = new Queue<Edge>(); visited = new boolean[v]; q = new MinQ<Edge>(); //先將原點0和他的周邊添加到隊列中做爲mst初始數據 visit(0, g); while (!q.isEmpty()) { // 利用貪心算法獲取權重最小的邊 Edge edge = q.poll(); int a = edge.oneSide(); int b = edge.otherSide(a); // 若是該邊兩個節點都被訪問過,就不能添加到優先隊列了,不然會造成環路 if (visited[a] && visited[b]) continue; // 將邊添加到最小生成樹中 mst.enqueue(edge); // 添加權重 weight += edge.weight(); //找到該節點中沒有被訪問的那個頂點,並訪問它和他的邊,若是沒有找到,那麼就從優先隊列中取出數據並訪問之 if (!visited[a]) visit(a, g); if (!visited[b]) visit(b, g); } } /** * * @Title: visit * @Description: 訪問該節點,並將該節點的不會造成環路的邊添加到優先隊列中 * @param @param v * @param @param g 設定文件 * @return void 返回類型 * @throws */ public void visit(int v, WeightGraph g) { visited[v] = true; for (Edge edge : g.adj(v)) { // 獲取該邊除了s之外另外一個頂點 int other = edge.otherSide(v); // 若是沒有訪問過,就添加到優先隊列中 if (!visited[other]) { q.offer(edge); } } } //最小生成樹的邊 public Iterable<Edge> mst() { return mst; } //權重 public double weight() { return weight; } public static void main(String[] args) { In in = new In("F:\\算法\\attach\\tinyEWG.txt"); WeightGraph g = new WeightGraph(in); PrimMST m = new PrimMST(g); for (Edge e : m.mst()) { System.out.println("邊:"+e); } System.out.println(m.weight()); } }
如下爲該算法所依賴的數據結構: java
package org.loda.graph; /** * * @ClassName: Edge * @Description: 帶有權重的邊 * @author minjun * @date 2015年5月25日 下午8:44:57 * */ public class Edge implements Comparable<Edge>{ //權重 private double weight; //一邊的頂點 private int v; //另外一邊的頂點 private int other; public Edge( int v, int other,double weight) { super(); this.v = v; this.other = other; this.weight = weight; } /** * * @Title: oneSide * @Description: 該邊一端的頂點 * @param @return 設定文件 * @return int 返回類型 * @throws */ public int oneSide(){ return v; } /** * * @Title: otherSide * @Description: 該邊另外一端的頂點 * @param @param a * @param @return 設定文件 * @return int 返回類型 * @throws */ public int otherSide(int a){ if(a==v) return other; else if(a==other) return v; else throw new IllegalArgumentException("參數應該是該邊的其中一個頂點值"); } /** * * @Title: weight * @Description: 該邊權重值 * @param @return 設定文件 * @return double 返回類型 * @throws */ public double weight(){ return weight; } @Override public int compareTo(Edge o) { double result=this.weight-o.weight; if(result<0) return -1; else if(result>0) return 1; else return 0; } @Override public String toString() { return v+"-"+other+"\t"+weight; } }
package org.loda.graph; import org.loda.structure.Bag; import org.loda.structure.Queue; import org.loda.util.In; /** * * @ClassName: WeightGraph * @Description: 帶權重的無向圖 * * 因爲帶邊是有權重的,因此若是像以前那樣以頂點來添加和描述這個圖顯然不太合適了,這裏就引入了另外的對象Edge邊,它擁有權重屬性 * * @author minjun * @date 2015年5月25日 下午8:31:13 * */ public class WeightGraph { // 頂點數量 private int v; //邊的數量 private int e; // 揹包 private Bag<Edge>[] bag; //全部的邊 private Queue<Edge> edges; @SuppressWarnings("unchecked") public WeightGraph(int v) { this.v = v; bag = new Bag[v]; edges=new Queue<Edge>(); for (int i = 0; i < v; i++) { bag[i] = new Bag<Edge>(); } } public WeightGraph(In in) { this(in.readInt()); int m = in.readInt(); for (int i = 0; i < m; i++) { add(in.readInt(), in.readInt(), in.readDouble()); } } public void add(int a, int b, double weight) { Edge edge=new Edge(a, b, weight); bag[a].add(edge); bag[b].add(edge); edges.enqueue(edge); e++; } public Iterable<Edge> adj(int a) { return bag[a]; } public Iterable<Edge> edges(){ return edges; } public int v() { return v; } public int e(){ return e; } }
8
16
4 5 0.35
4 7 0.37
5 7 0.28
0 7 0.16
1 5 0.32
0 4 0.38
2 3 0.17
1 7 0.19
0 2 0.26
1 2 0.36
1 3 0.29
2 7 0.34
6 2 0.40
3 6 0.52
6 0 0.58
6 4 0.93
算法
輸出結果爲: 數據結構
邊:0-7 0.16 邊:1-7 0.19 邊:0-2 0.26 邊:2-3 0.17 邊:5-7 0.28 邊:4-5 0.35 邊:6-2 0.4 1.81