算法——克鲁斯卡尔算法(Java代码实现)

算法——普利姆算法(Java代码实现)
与前文相同的问题处理:
在这里插入图片描述
某城市新增7个站点(A, B,C,D,E,F,G),现在需要修路把7个站点连通
各个站点的距离用边线表示(权),比如A-B距离12公里
问:如何修路保证各个站点都能连通,并且总的修建公路总里程最短?

克鲁斯卡尔算法:
  1. 克鲁斯卡尔(Kruskal)算法,是用来求加权连通图的最小生成树的算法。
  2. 基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路
  3. 具体做法:首先构造-一个只含n个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止

代码实现:

package KruskalAlgorithm;

import java.util.Arrays;

public class Kruskal {
	private int edges;
	private char[] vertexs;
	private int[][] arr;
	// private static final int INF = Integer.MAX_VALUE;
	private static final int INF = 0;

	public Kruskal(char[] vertexs, int[][] arr) {
		// 顶点个数
		int v = vertexs.length;

		// 初始化顶点的值
		this.vertexs = new char[v];
		for (int i = 0; i < arr.length; i++) {
			this.vertexs[i] = vertexs[i];
		}
		// 边
		this.arr = new int[v][v];
		for (int i = 0; i < v; i++) {
			for (int j = 0; j < v; j++) {
				this.arr[i][j] = arr[i][j];
			}
		}
		// 统计边
		for (int i = 0; i < v; i++) {
			for (int j = i + 1; j < v; j++) {
				if (this.arr[i][j] != INF) {
					edges++;
				}
			}
		}
	}

	public void show() {
		System.out.println("邻接矩阵:");
		for (int i = 0; i < vertexs.length; i++) {
			for (int j = 0; j < vertexs.length; j++) {
				System.out.printf("%4d", arr[i][j]);
			}
			System.out.println();
		}
	}

	// 对边的权值进行排序
	public void sort(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 temp = edges[j];
					edges[j] = edges[j + 1];
					edges[j + 1] = temp;
				}
			}
		}
	}

	// 根据顶点返回对应的下标
	public int getPosition(char ch) {
		for (int i = 0; i < vertexs.length; i++) {
			if (vertexs[i] == ch) {
				return i;
			}
		}
		return -1;
	}

	// 获取图的边,放入数组当中,最后遍历数组
	public EData[] getEdges() {
		int index = 0;
		EData[] e = new EData[edges];
		for (int i = 0; i < vertexs.length; i++) {
			for (int j = i + 1; j < vertexs.length; j++) {
				if (this.arr[i][j] != INF) {
					// 把这条边加入到数组当中
					e[index++] = new EData(vertexs[i], vertexs[j], arr[i][j]);
				}
			}
		}
		return e;
	}

	// 获取下标为i的顶点的终点
	public int getEnd(int[] ends, int i) {
		while (ends[i] != 0) {
			i = ends[i];
		}
		return i;
	}

	public void kruskalAlgorithm() {
		int index = 0;// 最后结果数组的索引
		int[] ends = new int[edges]; // 用于保存已有的最小生成树中的终点
		// 创建结果数组
		EData[] res = new EData[edges];
		// 获取原始图所有边的集合
		EData[] elist = getEdges();
		System.out.println("elist =" + Arrays.toString(elist) + " , 边 = " + elist.length);
		// 对边进行排序
		sort(elist);
		// 遍历数组,判断准备加入的边是否构成回路
		for (int i = 0; i < edges; i++) {
			// 获取第i条边的第一个顶点和终点
			int p1 = getPosition(elist[i].start);
			int p2 = getPosition(elist[i].end);
			// 对这第一个顶点在最小生成树当中的终点
			int m = getEnd(ends, p1);
			int n = getEnd(ends, p2);
			// 判断是否会构成回路
			if (m != n) { //没有构成
				ends[m] = n;
				res[index++]=elist[i];  //把一条边加入 
			}
		}
		//统计并打印最小生成树
		System.out.println("最小生成树 = ");
		for (int i = 0; i < index; i++) {
			System.out.println(res[i]);
		}
		
	}
	

	public static void main(String[] args) {
		char[] vertexs = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
		int[][] arr = { { 0, 12, INF, INF, INF, 16, 14 }, { 12, 0, 10, INF, INF, 7, INF }, { INF, 10, 0, 3, 5, 6, INF },
				{ INF, INF, 3, 0, 4, INF, INF }, { INF, INF, 5, 4, 0, 2, 8 }, { 16, 7, 6, INF, 2, 0, 9 },
				{ 14, INF, INF, INF, 8, 9, 0 } };
		Kruskal kruskal = new Kruskal(vertexs, arr);
		kruskal.show();
		EData[] edges = kruskal.getEdges();
		System.out.println("未排序 = " + Arrays.toString(edges));
		kruskal.sort(edges);
		System.out.println("排序 = " + Arrays.toString(edges));
		kruskal.kruskalAlgorithm();
	}
}

//创建一个类,用于表示一条边,俩个顶点和权值
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;
	}

	@Override
	public String toString() {
		return "EData[<" + start + " - " + end + "> =" + weight + "]";
	}
}

测试代码:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Modify_QmQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值