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