目录
概述
代码实现
public class KruskalCase {
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}};
//大家可以在去测试其它的邻接矩阵,结果都可以得到最小生成树.
//创建KruskalCase 对象实例
KruskalCase kruskalCase = new KruskalCase(vertexs, matrix);
//输出构建的
kruskalCase.print();
kruskalCase.kruskal();
/*
// 测试排序
EData[] edges = kruskalCase.getEdges();
System.out.println("排序前: " + Arrays.toString(edges));
kruskalCase.sortEdges(edges);
System.out.println("排序后: " + Arrays.toString(edges));
*/
}
/**
* 初始化
* @param vertexs 节点名称
* @param matrix 边集合
*/
public KruskalCase(char[] vertexs, int[][] matrix){
int vLen = vertexs.length; // 节点数
// 创建节点
this.vertexs = new char[vLen];
for (int i = 0; i < vLen; 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];
// 计算连通边长度
// 这里的 j >= i + 1 是为了不重复计算边, 例如 ['A','B', 12], ['B','A',12]
if(matrix[i][j] != INF && j >= i + 1 ){
this.edgeNum++;
}
}
}
}
/**
* Kruskal 算法本体
*/
public void kruskal(){
int index = 0; // 用于记录结果数组的下标
int[] ends = new int[edgeNum]; // 定义 大小为边数 的终点边集
// 定义最小树 结果集, n个点, n-1条边 -> vertexs.length - 1
EData[] result = new EData[vertexs.length - 1];
/*
思路:
1. 先对边集合进行 从小到大 的排序
2. 从小开始找路:
先得到每条边的起点和终点
如果这两个点的终点指向同一个尾结点, 则不能加入最小树中
否则 把这两个点置为互为起点与终点
*/
EData[] edges = getEdges();
System.out.println("排序前: " + Arrays.toString(edges));
sortEdges(edges);
System.out.println("排序后: " + Arrays.toString(edges));
System.out.println("当前边的数量: " + edges.length);
// 遍历每个节点, 查找他的终点是否有相等
for (int i = 0; i < edgeNum; i++) {
// 第i条边的起点
int m1 = getPosition(edges[i].start);
// 第i条边的终点
int m2 = getPosition(edges[i].end);
// 第i条边的起点 构成的路径 的终点
int e1 = getEnd(ends, m1);
// 第i条边的终点 构成的路径 的终点
int e2 = getEnd(ends, m2);
if(e1 != e2){
// 两个点构成的路径终点不同, 说明不会构成环, 可以加入
ends[e1] = e2;
result[index++] = edges[i];
}
}
//<E,F> <C,D> <D,E> <B,F> <E,G> <A,B>。
//统计并打印 "最小生成树", 输出 rets
System.out.println("最小生成树为");
for(int i = 0; i < index; i++) {
System.out.println(result[i]);
}
}
/**
*
* @param ch 顶点的值,比如'A','B'
* @return 返回ch顶点对应的下标,如果找不到,返回-1
*/
private int getPosition(char ch) {
for(int i = 0; i < vertexs.length; i++) {
if(vertexs[i] == ch) {//找到
return i;
}
}
//找不到,返回-1
return -1;
}
/**
* 功能:对边进行排序处理, 冒泡排序
* @param edges 边的集合
*/
public void sortEdges(EData[] edges) {
for (int i = 0; i < edges.length; 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;
}
}
}
}
/**
* 功能: 获取图中边,放到EData[] 数组中
* 是通过matrix 邻接矩阵来获取
* EData[] 形式 [['A','B', 12], ['B','F',7], .....]
* @return
*/
public EData[] getEdges() {
EData[] edges = new EData[edgeNum];
int index = 0;
for (int i = 0; i < matrix.length; i++) {
// 上三角遍历方式, 把所有边和点对应绑定
for (int j = i + 1; j < matrix[0].length; j++) {
if(matrix[i][j] != INF){
edges[index++] = new EData(vertexs[i], vertexs[j], matrix[i][j]);
}
}
}
return edges;
}
/**
* 功能: 获取下标为i的顶点的终点()
* 用于后面判断两个顶点的终点是否相同
* @param ends : 数组就是记录了各个顶点对应的下一个节点是哪个,ends 数组是在遍历过程中,逐步形成, 并且最终指向的
* 就是最开始传入的 i 的最后一个节点
* @param i : 表示传入的顶点对应的下标
* @return 返回的就是 下标为i的这个顶点对应的终点的下标
*/
private int getEnd(int[] ends, int i) { // i = 4 [0,0,0,0,5,0,0,0,0,0,0,0]
while (ends[i] != 0){
// 通过这样不断深入找下一个节点的下标
i = ends[i];
}
return i;
}
/**
* 遍历输出
*/
public void print(){
System.out.println("邻接矩阵为: \n");
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();//换行
}
}
}
/**
* 边类
* 用于保存每条边对应的顶点和权值
*/
class EData{
char start; //边的一个点
char end; //边的另外一个点
int weight; // 权值
@Override
public String toString() {
return "EData{" +
"start=" + start +
", end=" + end +
", weight=" + weight +
'}';
}
public EData(char start, char end, int weight) {
this.start = start;
this.end = end;
this.weight = weight;
}
}