package com.ldp.algorithm.demo05Kruskal;
import org.junit.Test;
import java.util.Arrays;
/**
* @create 06/14 9:04
* @description <P>
* 克鲁斯卡尔算法-公交站问题
* 1.找出所有边
* 2.对边按照权值排序
* 3.从小到大加入边(要求加入时不构成回路)
* 是否构成回路的判定标准
* 将要加入的边的"终点" 不相等,(这句话很抽象,难以使用文字表述,大家理解下面的代码吧)
* 那么这个算法最核心,最难的点其实就是如何动态的获取"终端"的算法
* </P>
*/
public class Test01 {
@Test
public void test01() {
// 1.定义节点
char[] nodes = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
// 2.使用矩阵表示节点间的距离
// -1表示节点之间不通,0表示自己节点的距离,如A到A的距离为0
int def = -1;
int[][] nodeSpace = {
// 'A', 'B', 'C', 'D', 'E', 'F', 'G'
{0, 12, def, def, def, 16, 14},
{12, 0, 10, def, def, 7, def},
{def, 10, 0, 3, 5, 6, def},
{def, def, 3, 0, 4, def, def},
{def, def, 5, 4, 0, 2, 8},
{16, 7, 6, def, 2, 0, 9},
{14, def, def, def, 8, 9, 0}
};
MinTree minTree = new MinTree(nodes, nodeSpace);
// minTree.printNodeSpace();
// minTree.getEdgeBySpace();
// minTree.printEdges();
// minTree.sortEdges();
// minTree.printEdges();
minTree.kruskal();
minTree.printEdgesResult();
}
}
class MinTree {
// 1.定义节点
char[] nodes;
// 2.使用矩阵表示节点间的距离
// -1表示节点之间不通,0表示自己节点的距离,如A到A的距离为0
int def = -1;
// 节点间的距离关系
int[][] nodeSpace;
// 边,应排序
Edge[] edges;
// 边的条数
int edgeNum;
// 实际选出来的最优边
Edge[] edgesResult;
/**
* 采用复制的方式初始化对象
*
* @param nodes
* @param nodeSpace
*/
public MinTree(char[] nodes, int[][] nodeSpace) {
int length = nodes.length;
// 节点处理
char[] nodesNew = new char[length];
for (int i = 0; i < length; i++) {
nodesNew[i] = nodes[i];
}
this.nodes = nodesNew;
// 位置距离处理和有效边的条数
int[][] nodeSpaceNew = new int[length][length];
int num = 0;
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++) {
nodeSpaceNew[i][j] = nodeSpace[i][j];
if (i < j && nodeSpace[i][j] != def) {// 统计边的条数
num++;
}
}
}
this.nodeSpace = nodeSpace;
this.edgeNum = num;
}
/**
* 打印矩阵
*/
public void printNodeSpace() {
for (int[] ints : nodeSpace) {
System.out.println(Arrays.toString(ints));
}
}
/**
* 根据位置空间获取边
*/
public void getEdgeBySpace() {
if (edges != null) {
return;
}
Edge[] edgesNew = new Edge[edgeNum];
// 遍历位置空间数组
int length = nodes.length;
int num = 0;
for (int i = 0; i < length; i++) {
for (int j = i + 1; j < length; j++) {
int weight = nodeSpace[i][j];
if (weight == def) {// 说明不通,不是有效边
continue;
}
Edge edge = new Edge();
edge.weight = weight;
edge.start = i;
edge.end = j;
edge.name = String.valueOf(nodes[i]) + String.valueOf(nodes[j]);
edgesNew[num] = edge;
num++;
}
}
this.edges = edgesNew;
}
/**
* 打印边
*/
public void printEdges() {
System.out.println("================edges 打印开始======================");
for (Edge edge : edges) {
System.out.println(edge);
}
System.out.println("================edges 打印结束======================");
}
/**
* 对边排序,从小到大
*/
public void sortEdges() {
for (int i = 0; i < edgeNum - 1; i++) {
for (int j = i + 1; j < edgeNum; j++) {
int weightI = edges[i].weight;
int weightJ = edges[j].weight;
if (weightI > weightJ) {
Edge temp = edges[i];
edges[i] = edges[j];
edges[j] = temp;
}
}
}
}
/**
* 获取第i个元素的终点下标
*
* @param ends
* @param i
* @return
*/
public int getEndIndex(int[] ends, int i) {
// 注意这里是while循环,对于链接成穿的边会循环找,如C-D-E-F这三个边形成后,C,D,E的终点都是F
while (ends[i] != 0) {
i = ends[i];
}
return i;
}
/**
* 克鲁斯卡尔算法实现
*/
public void kruskal() {
// 生成边
getEdgeBySpace();
// 排序
sortEdges();
// 遍历边依次加入到结果集
Edge[] edgesResult = new Edge[nodes.length - 1];
int num = 0;
// 记录终点
int[] ends = new int[nodes.length];
for (int i = 0; i < edgeNum; i++) {
Edge edge = edges[i];
int startIndex = getEndIndex(ends, edge.start);
int endIndex = getEndIndex(ends, edge.end);
if (startIndex == endIndex) {
// 同一个终点会构成回路,不加入
continue;
}
System.out.println("添加边:num=" + num + ",edge=" + edge);
// 加入边
edgesResult[num++] = edge;
// 加入终点
// ends[edge.start] = endIndex;
ends[startIndex] = endIndex;
}
this.edgesResult = edgesResult;
}
/**
* 打印边
*/
public void printEdgesResult() {
System.out.println("================edges 打印开始======================");
for (Edge edge : edgesResult) {
System.out.println(edge);
}
System.out.println("================edges 打印结束======================");
}
}
class Edge {
String name;
int start;
int end;
int weight;
@Override
public String toString() {
return "=>" + name + "=" + weight;
}
}