算法导论——最小生成树:Kruskal算法(利用了并查集)

醉酒当歌 提交于 2020-01-07 15:11:19

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

package org.loda.graph;

import org.loda.structure.MinQ;
import org.loda.structure.Queue;
import org.loda.util.In;

/**
 * 
* @ClassName: KruskalMST 
* @Description:Kruskal最小生成树算法 
* @author minjun
* @date 2015年5月25日 下午10:50:01 
*
 */
public class KruskalMST {
	
	private Queue<Edge> mst;
	
	private double weight;
	
	public KruskalMST(WeightGraph g){
		int v=g.v();
		
		mst=new Queue<Edge>();
		
		MinQ<Edge> q=new MinQ<Edge>();
		
		UF uf=new UF(v);
		
		//将所有边添加到优先队列中
		for(Edge edge:g.edges()){
			q.offer(edge);
		}
		
		while(!q.isEmpty()){
			Edge edge=q.poll();

			int a=edge.oneSide();
			int b=edge.otherSide(a);
			
			//如果已经连通,说明这条边无法继续加入,否则会形成环路
			if(uf.connected(a, b)) continue;
			
			//将两个连通分量连通
			uf.union(a, b);
			
			mst.enqueue(edge);
			
			weight+=edge.weight();
		}
	}
	
	/**
	 * 
	* @Title: edges 
	* @Description: 最小生成树的所有边 
	* @param @return    设定文件 
	* @return Iterable<Edge>    返回类型 
	* @throws
	 */
	public Iterable<Edge> edges(){
		return mst;
	}
	
	/**
	 * 
	* @Title: weight 
	* @Description: 最小生成树的权重 
	* @param @return    设定文件 
	* @return double    返回类型 
	* @throws
	 */
	public double weight(){
		return weight;
	}
	
	public static void main(String[] args) {
		In in = new In("F:\\算法\\attach\\tinyEWG.txt");
		WeightGraph g = new WeightGraph(in);
		KruskalMST m = new KruskalMST(g);
		for (Edge e : m.edges()) {
			System.out.println("边:"+e);
		}
		System.out.println(m.weight());

	}
}

所依赖的数据结构和前一篇的Prim算法相同,都是带权重边的无向图,和一条权重边,不过由于Kruskal算法的核心思想是不断找到最小的边,并查看他们连通与否,如果不连通,则合并他们成同一个连通分量,并将这条边加入到最小生成树。需要检测无向图的连通性,则需要利用到另外一个算法union-find并查集,下面给出并查集的简单实现

package org.loda.graph;

/**
 * 
* @ClassName: UF 
* @Description:并查集 
* @author minjun
* @date 2015年5月25日 下午11:41:16 
*
 */
public class UF {

	//每个元素的标识
	private int[] id;
	
	//连通分量数量
	private int count;
	
	public UF(int n){
		id=new int[n];
		
		//初始化为n个不相连的点(连通分量)的值
		count=n;
		for(int i=0;i<n;i++){
			id[i]=i;
		}
	}

	/**
	 * 
	* @Title: connected 
	* @Description: 判断两点是否连通 
	* @param @param a
	* @param @param b
	* @param @return    设定文件 
	* @return boolean    返回类型 
	* @throws
	 */
	public boolean connected(int a,int b){
		return find(a)==find(b);
	}
	
	/**
	 * 
	* @Title: union 
	* @Description: 合并两个连通分量 
	* @param @param a
	* @param @param b    设定文件 
	* @return void    返回类型 
	* @throws
	 */
	public void union(int a,int b){
		int A=find(a);
		int B=find(b);
		
		//如果相等,表示已经合并成一个连通分量了
		if(A==B) return;
		
		//合并所有属于b的连通分量为a
		for(int i=0;i<id.length;i++){
			if(id[i]==B)
				id[i]=A;
		}
		
		count--;
	}
	
	/**
	 * 
	* @Title: find 
	* @Description: 查找连通分量的值 
	* @param @param a
	* @param @return    设定文件 
	* @return int    返回类型 
	* @throws
	 */
	public int find(int a){
		return id[a];
	}
	
	public int count(){
		return count;
	}
	
	public static void main(String[] args) {
		UF qf=new UF(10);
		qf.union(4, 3);
		qf.union(3, 8);
		qf.union(6, 5);
		qf.union(9, 4);
		qf.union(2, 1);
		qf.union(5, 0);
		qf.union(7, 2);
		qf.union(6, 1);
		qf.union(1, 0);
		qf.union(6, 7);
		
		System.out.println("connected :"+qf.count()+","+qf.connected(5, 9));
	}
}



输入的测试数据为:

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
边:2-3	0.17
边:1-7	0.19
边:0-2	0.26
边:5-7	0.28
边:4-5	0.35
边:6-2	0.4
1.81




易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!