七、克鲁斯卡尔算法




逻辑:
获取排序后边的集合,保证优先添加权值最小的边,通过回路判断(终点是否一致)决定是否将边加入最终结果集
代码实现:
package Algorithm.Kruskal;
import java.lang.reflect.Array;
import java.util.Arrays;
//克鲁斯卡尔算法
public class KK {
int edgeNum;//边的数量
char[] vertex;//顶点数组
int[][] matrix;//邻接矩阵
EData[] edges;//边的集合
//使用INF表示2个节点不能连通
final static int INF = Integer.MAX_VALUE;
public static void main(String[] args) {
char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
int[][] matrix = {
{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}
};
KK kk = new KK(vertex, matrix);
// kk.print();
kk.sortEdge(kk.edges);
// System.out.println(Arrays.toString(kk.edges));
kk.kruskal();
}
//初始化
public KK(char[] vertex, int[][] matrix) {
this.vertex = vertex;
this.matrix = matrix;
//统计边(创建一个边权值的集合方便遍历)
for (int i = 0; i < vertex.length; i++) {
for (int j = 0; j < vertex.length; j++) {
if (this.matrix[i][j] != INF&& this.matrix[i][j] != 0) {
edgeNum++;//此时统计数量重复,后续创建边的数组时要/2
}
}
}
this.edges = new EData[edgeNum/2];
int index = 0;
for (int i = 0; i < vertex.length; i++) {
for (int j = i+1; j < vertex.length; j++) {//j=i+1的主要目的是去重(矩阵的右三角)
if (this.matrix[i][j] != INF) {
edges[index++] = new EData(vertex[i],vertex[j],matrix[i][j]);//看这儿!!!start在这儿设的
}
}
}
}
//打印
public void print() {
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix.length; j++) {
if (matrix[i][j] == INF) {
System.out.printf("INF");
} else
System.out.print(matrix[i][j]);
}
System.out.println();
}
}
//对边进行排序处理
public void sortEdge(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 < vertex.length; i++) {
if (vertex[i]==ch){
return i;
}
}
return -1;
}
//获取下标为i的顶点的终点(用于判断是否形成回路)
public int getEnd(int[] ends,int i){
while (ends[i]!=0){
i = ends[i];//已有通路上的顶点的终点都会统一一直找到为终点为0的那个顶点(还没有以该顶点为起点的边)
}
return i;//如果数组中没有值,那么自己就是终点
}
public void kruskal(){
int index =0;//最后结果数组的索引
int ends[] = new int[edgeNum];//用于保存已有的每个顶点在最小生成树的终点
//创建结果数组
EData[] res = new EData[edgeNum/2];
//按照边的权值大小排序
sortEdge(edges);
//将符合要求的边添加到最小生成树的结果数组中
for (int i = 0; i < edges.length; i++) {//排序后先出来的就是权值最小的边
int p1 = getPosition(edges[i].start);
int p2 = getPosition(edges[i].end);
int m = getEnd(ends,p1);
int n = getEnd(ends,p2);
//判断是否构成回路
if (m!=n){
ends[m] = n;
res[index++] = edges[i];//有一条边加入了res数组
}
}
//输出res数组
System.out.println(Arrays.toString(res));
}
}
//创建一个EData类,他的实例对象表示一条边
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=" + start +
", end=" + end +
", weight=" + weight +
'}';
}
}