算法導論——最小生成樹: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());

	}
}

如下爲該算法所依賴的數據結構: 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
相關文章
相關標籤/搜索