算法导论——最小生成树:Prim算法

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



转载于:https://my.oschina.net/u/1378920/blog/419903

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值