最小生成树的实现算法

本文深入探讨了图论中的经典问题——最小生成树,详细介绍了Prim算法和Kruskal算法的实现过程。通过代码示例,展示了如何使用并查集进行路径选择,以及基于Dijkstra算法改进的Prim算法的应用。
摘要由CSDN通过智能技术生成

最小生成树是图论中的经典问题,我们常用的算法基于dikstra算法改进的prim算法,还有一种就是利用并查集实现的kruska算法了。

下面我们来看看代码实现:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * 最小生成树
 * 1.Prim算法
 * 2.Kruska算法
 * @author Administrator
 *
 */
public class MST {

	private static int[][] costs = {
		{0,1,Integer.MAX_VALUE,9,Integer.MAX_VALUE,Integer.MAX_VALUE,2},
		{1,0,8,1,3,Integer.MAX_VALUE,Integer.MAX_VALUE },
		{Integer.MAX_VALUE,8,0,Integer.MAX_VALUE,6,Integer.MAX_VALUE,Integer.MAX_VALUE },
		{9,1,Integer.MAX_VALUE,0,5,5,3},
		{Integer.MAX_VALUE,3,6,5,0,8,Integer.MAX_VALUE},
		{Integer.MAX_VALUE,Integer.MAX_VALUE,Integer.MAX_VALUE,5,8,0,10} ,	
		{2,Integer.MAX_VALUE,Integer.MAX_VALUE,3,Integer.MAX_VALUE,10,0}	};
	
	
	public static void main(String[] args) {
		long x = System.currentTimeMillis(),y;
		prim();
		y = System.currentTimeMillis();
		System.out.println("用时:"+(y-x)+"ms");
		
		 x = System.currentTimeMillis();
		kruska();
		y = System.currentTimeMillis();
		System.out.println("用时:"+(y-x)+"ms");
	}
	/**
	 * prim算法求最小生成树,算法是基于dikstra算法改进的
	 * @param num
	 * @param isUsed
	 * @param pre
	 * @param costs
	 * @param d
	 */
	private static void prim() {
		int shortestPath = 0;
		int num = 7;
		int[] d = new int[num];
		//标示source到其它节点是否已经求出最短路径
		 boolean[] isUsed = new boolean[num];
		//路径还原时用,存放路径中上一个节点下标
		 int[] pre = new int[num];
		
		//初始化
		for (int i = 0; i < num; i++) {
			isUsed[i] = false;
		}
		for (int i = 0; i < num; i++) {
			pre[i] = -1;
		}
		fill(num,d);
		while (true) {
			int i = getMinIndex(isUsed, num, d);
			if (i == -1) {
				break;
			}
			//i号节点是未加入生成树的节点中离树最近的节点,先将其加入树中
			isUsed[i] = true;
			shortestPath+=d[i];
			for (int j = 0; j < num; j++) {
				if (!isUsed[j] && (d[j] > costs[i][j])) {
					d[j] = costs[i][j];
					//存放上一节点下标
					pre[j] = i;
				}
			}
		}
		System.out.println("prim算法最小生成树路径信息:");
		 for(int i=1;i<num;i++){
			 System.out.println("结点"+i+"---"+"结点"+pre[i]);
		 }
		 System.out.println("prim算法最小生成树路径总和:"+shortestPath);
	}
	/**
	 * 在未加入最小生成树的所有节点中选出离生成树最近的节点
	 * @param isShort
	 * @param num
	 * @param d
	 * @return
	 */
	private static int getMinIndex(boolean[] isUsed, int num, int[] d) {
		int i = -1;
		for (int u = 0; u < num; u++) {
			if (!isUsed[u] && (i == -1 || d[u] < d[i]))
				i = u;
		}
		return i;
	}
	/**
	 * 距离初始化
	 * @param s
	 * @param num
	 * @param costs
	 * @param d
	 */
	private static void fill(int num,int[] d) {
		for (int i = 0; i < num; i++) {
			d[i] = Integer.MAX_VALUE;
		}
		//从0号节点开始
		d[0] = 0;
	}
	/**
	 * Kruska算法
	 */
	private static void kruska(){
		int num = 7;
		int shortestPath = 0;
		//并查集
		BingChaSet bingChaSet = new BingChaSet(num);
		List<Route> list = new ArrayList();
		//向集合中添加路径信息
		for(int i=0;i<num-1;i++){
			for(int j=i+1;j<num;j++){
				if(costs[i][j]!=Integer.MAX_VALUE){
					list.add(new Route(i,j,costs[i][j]));
				}
			}
		}
		//根据权重排序
		if(list.size()>0){
			Collections.sort(list, new MyCompartor());
		}
		num = list.size();
		System.out.println("kruska算法最小生成树路径信息:");
		for(int i=0;i<num;i++){
			Route r = list.get(i);
			if(!bingChaSet.isSame(r.first,r.second )){
				//如果两个节点不在同一组则选取为生成树中的路径
				bingChaSet.merge(r.first, r.second);
				shortestPath+=r.priority;
				System.out.println("结点"+r.first+"---"+"结点"+r.second);
			}
		}
		
		 System.out.println("kruska算法最小生成树路径总和:"+shortestPath);
	}
	/**
	 * 路径信息封装类
	 * @author Administrator
	 *
	 */
	static class Route{
		int first,second,priority;
		public Route(int first,int second,int priority){
			this.first = first;
			this.second = second;
			this.priority = priority;
		}
	}
	/**
	 * 比较器
	 * @author Administrator
	 *
	 */
	static class MyCompartor implements Comparator<Route>{
		@Override
		public int compare(Route o1, Route o2) {
			if(o1.priority>o2.priority)
				return 1;
			else if(o1.priority<o2.priority)
				return -1;
			return 0;
		}
	}
	/**
	 * 并查集
	 * @author Administrator
	 *
	 */
	static class BingChaSet{
		private int par[],height[];
		public BingChaSet(int num) {
			par = new int[num];
			height = new int[num];
			for(int i=0;i<num;i++){
				par[i] = i;
				height[i] = 0;
			}
		}
		
		 //得到父亲节点的index
		private int getParentIndex(int i){
			if(par[i]==i)
				return i;
			else
				return par[i] = getParentIndex(par[i]);
		}
		
		//判断两节点是否属于同一组
		public boolean isSame(int i,int j){
			return getParentIndex(i)==getParentIndex(j);
		}
		//合并两节点所在的组
		public void merge(int i,int j){
			int x = getParentIndex(i);
			int y = getParentIndex(j);
			if(x==y)
				return;
			if(height[x]>height[y]){
				par[y] = x;
			}else{
				par[x] = y;
				if(height[x]==height[y]){
					height[y]++;
				}
			}
		}
		
	}
	
	
}


转载于:https://my.oschina.net/u/2434774/blog/646074

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值