一.kruskal算法
与prim算法一样,也是寻找图的最小生成树算法的一种,prim算法是将点加入结果,而kruskal算法是将边加入结果
核心思想就是,将边按权值排序后,最小的边加入结果集,且要求过程中已确定的边不构成回路
二.演示
三.问题解决
1.排序
这个不说了,但是得按权值来排
2.如何判定不构成回路
确定每个点的终点是否相同,如上图的第二张图,C,D,E的终点都是F,所以C不能与E相连,不能与F相连
四.代码实现
class KruskalCase {
//边的数量
private int edgeNum;
//顶点数组
private char[] vertexs;
//邻接矩阵
private int[][] matrix;
//使用INF表示两个顶点不能连通
private static final int INF = Integer.MAX_VALUE;
//最后结果数组的索引
int resultIndex = 0;
//创建结果数组,保存最后的最小生成树
EdgeData[] results;
public KruskalCase(char[] vertexs, int[][] matrix) {
this.vertexs = vertexs;
this.matrix = matrix;
//统计边的条数
for (int i = 0; i < vertexs.length; i++) {
for (int j = i + 1; j < vertexs.length; j++) {
if (this.matrix[i][j] != INF) {
edgeNum++;
}
}
}
}
/**
* 更具权值进行冒泡排序
* @param edgeData 边的集合
*/
public void sortEdges(EdgeData[] edgeData) {
for (int i = 0; i < edgeData.length - 1; i++) {
for (int j = 0; j < edgeData.length - 1 - i; j++) {
//下一条边的权值 > 当前边的权值 是进行交换
if (edgeData[j].weight > edgeData[j + 1].weight) {
EdgeData temp = edgeData[j];
edgeData[j] = edgeData[j + 1];
edgeData[j + 1] = temp;
}
}
}
}
/**
* 根据顶点的值查找顶点的下标
* @param c 顶点的值
* @return 顶点的下标, 未找到则返回-1
*/
private int getPosition(char c) {
//此处可优化
for (int i = 0; i < vertexs.length; i++) {
if (vertexs[i] == c) {
return i;
}
}
return -1;
}
/**
* 获取所有边
* @return 所有边
*/
private EdgeData[] getEdges() {
int index = 0;
EdgeData[] edgeData = new EdgeData[edgeNum];
for (int i = 0; i < vertexs.length; i++) {
for (int j = i + 1; j < vertexs.length; j++) {
if (matrix[i][j] != INF) {
edgeData[index++] = new EdgeData(vertexs[i], vertexs[j], matrix[i][j]);
}
}
}
return edgeData;
}
/**
* 根据顶点下标返回终点下标
* 用于判断两个顶点的终点是否相同
* @param ends 记录各个顶点对应的终点
* @param i 顶点的下标
* @return 顶点的终点的下标
*/
private int getEndIndexByStartIndex(int[] ends, int i) {
while (ends[i] != 0) {
i = ends[i];
}
return i;
}
/**
* 克鲁斯卡尔算法
*/
public void kruskal() {
//保存'已有最小生成树' 中的每个顶点在最小生成树中的终点
int[] ends = new int[edgeNum];
//获取图中所有边的集合
EdgeData[] edges = getEdges();
//按照边的权值大小排序
sortEdges(edges);
//初始化结果集
results = new EdgeData[edgeNum];
//遍历edges数组,判断带加入的边是否形成回路,形成回路不可加入,
// 未形成回路将其加入到结果数组result中
for (int i = 0; i < edgeNum; i++) {
//获取第i条边的第一个顶点
int a = getPosition(edges[i].start);
//获取第i条边的第二个顶点
int b = getPosition(edges[i].end);
//获取 peakIndex1 在已有最小生成树的终点
int m = getEndIndexByStartIndex(ends, a);
//获取 peakIndex2 在已有最小生成树的终点
int n = getEndIndexByStartIndex(ends, b);
//判断是否构成回路
if (m != n) {
//设置endIndex1 在'已有最小生成树' 中的终点
ends[m] = n;
results[resultIndex++] = edges[i];
}
}
}
/**
* 打印邻接矩阵
*/
public void printMatrix() {
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();
}
System.out.println("---------邻接矩阵---------");
}
/**
* 显示结果
*/
public void showResult() {
System.out.println("---------输出结果---------");
for (int i = 0; i < results.length; i++) {
if(results[i] != null){
System.out.println(results[i]);
}
}
System.out.println("---------输出结果---------");
}
}
/**
* 表示一条边
*/
class EdgeData {
//边的第一个点
char start;
//边的第二个点
char end;
//边的权值
int weight;
public EdgeData(char start, char end, int weight) {
this.start = start;
this.end = end;
this.weight = weight;
}
@Override
public String toString() {
return "EdgeData{" + start + " -> " + end + ", weight=" + weight + '}';
}
}