克鲁斯卡尔算法(Kruskal)

克鲁斯卡尔算法介绍
1)克鲁斯卡尔(Kruskal)算法, 是用来求加权连通图的最小生成树的算法。
2)基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回

3) 具体做法:首先构造一 个只含n个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止

克鲁斯卡尔算法与普利姆算法都是求最小生成树
而普利姆算法是任意顶点开始 选择权值最小的路径进行连通
克鲁斯卡尔算法是优先连通权值最小的路径

在这里插入图片描述

import java.util.Arrays;

public class KeuskalCase {

	private int edgeNum;//边的个数
	private char[] vertexs;//顶点数组
	private int[][] matrix;//邻接矩阵
	//使用INF表示两个定点不能连通
	private static final int INF = Integer.MAX_VALUE;
	
	
	public static void main(String[] args) {
		char[] vertexs = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
		int matrix[][] = { /* A *//* B *//* C *//* D *//* E *//* F *//* G */
			/* A */ { 0, 12, INF, INF, INF, 16, 14 }, 
			/* B */ { 12, 0, 10, INF, INF, 7, INF },
			/* C */ { INF, 10, 0, 3, 5, 6, INF }, 
			/* D */ { INF, INF, 3, 0, 4, INF, INF },
			/* E */ { INF, INF, 5, 4, 0, 2, 8 },
			/* F */ { 16, 7, 6, INF, 2, 0, 9 },
			/* G */ { 14, INF, INF, INF, 8, 9, 0 } };
		//创建对象
		KeuskalCase keuskalCase = new KeuskalCase(vertexs,matrix);
		keuskalCase.print();
		EData[] edges = keuskalCase.getEdges();
		//System.out.println(Arrays.toString(edges));
		keuskalCase.sortEdges(edges);
		//System.out.println(Arrays.toString(edges));
		keuskalCase.kruskal();
	}

	//构造器
	public KeuskalCase(char[] vertexs,int[][] matrix) {
		//初始化顶点数
		int vlen = vertexs.length;
		//初始化顶点 复制拷贝的方式
		this.vertexs = new char[vlen];
		for(int i = 0;i < vertexs.length;i++) {
			this.vertexs[i] = vertexs[i];
			
		}
		//初始化边(矩阵)
		this.matrix = new int[vlen][vlen];
		for(int i = 0;i < vlen;i++){
			for(int j= 0; j < vlen; j++) {
				this .matrix[i][j] = matrix[i][j] ;
			}
		}
		//统计边
		for(int i = 0;i < vlen;i++){
			for(int j= i+1;j < vlen; j++) {
				if(this.matrix[i][j] != INF) {
				edgeNum++;
				}
			}
		}
	}
	
	//打印邻接矩阵
	public void print() {
		System.out.println("邻接矩阵");
		for(int i = 0;i < vertexs.length;i++){
			for(int j= 0;j < vertexs.length ; j++) {
				System.out.printf("%12d",matrix[i][j]);
			}
			System.out.println();
		}
	}
	
	//对边的权值进行排序(冒泡)
	public void sortEdges(EData[] edges) {
		for(int i = 0;i < edges.length - 1 ;i++){
			for(int j= 0;j < edges.length - 1 - i ; j++) {
				if(edges[j].weight > edges[j+1].weight) {
					EData tmp = edges[j];
					edges[j] = edges[j+1];
					edges[j+1] = tmp;
				}
			
			}
			
		}
	}
	private int getPosition(char ch) {
		//ch顶点的值 返回该顶点下标
		for(int i = 0;i < vertexs.length;i++) {
			if(vertexs[i] == ch) {
				return i;
			}
		}
		//找不到返回 -1
		return -1;
	}
	//获得边的对象数组('a','v','3')()()...
	private EData[] getEdges() {
		//获取图中的边 放入数组 
		int index = 0;
		EData[] edges = new EData[edgeNum];
		for(int i = 0;i < vertexs.length;i++) {
			for(int j = i+1; j < vertexs.length;j++) {
				if(matrix[i][j] != INF) {
					edges[index++] = new EData(vertexs[i],vertexs[j],matrix[i][j]);
				}
			}
		}
		return edges;		
		
	}
	
	//获取下标i的点的终点
	//ends[] 记录各个顶点对应的终点是哪个 是在遍历过程中逐步形成的
	//i 传入的顶点对应的下标
	private int getEnd(int[] ends,int i) {
		if(ends[i] != 0) {
			i = ends[i];
		}
		return i;
	}
	
	public void kruskal() {
		int index = 0;//表示最后结果数组的索引
		int[] ends = new int[edgeNum];//用于保存已有最小生成树中的每个顶点的终点
		//创建结果数组 保存最后的最小生成树
		EData[] rets = new EData[edgeNum-1];
		
		//获取图中所有边的集合
		EData[] edges = getEdges();
		System.out.println(Arrays.toString(edges));
		sortEdges(edges);//对边排序
		//遍历edges数组,将边添加到最小生成树中时,判断是准备加入的边否形成了回路,如果没有,就加入rets,否则不能加入
		for(int i = 0;i < edgeNum;i++) {
			//遍历边
			int p1 = getPosition(edges[i].start);//得到边的一点的下标
			int p2 = getPosition(edges[i].end);//得到边的另一点的下标
			
			//获取p1 p2 的终点
			int m = getEnd(ends,p1);
			int n = getEnd(ends,p2);
			//判断是否构成回路
			if(m != n) {
				//未构成回路
				ends[m] = n;//设置m的终点为n
				rets[index++] = edges[i];//加入
			}		
		}
		//统计并打印rets
		System.out.println("最小生成书" + Arrays.toString(rets));
	}
	
}
//创建一个类 每个对象代表一条边
class EData{
	char start;//边的一个点
	char end;//边的另一个点
	int weight;//边的权值
	//构造器
	public EData(char start,char end,int weight) {
		this.start = start;
		this.end = end;
		this.weight = weight;
	}

	
	public String toString() {
		return "EData [start=" + start + ", end=" + end + ", weight=" + weight + "]";
	}
	
	
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值