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());
}
}
以下为该算法所依赖的数据结构:
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