package com.guigu.algorithm.kruskal;
import jdk.internal.org.objectweb.asm.tree.MultiANewArrayInsnNode;
import java.util.Arrays;
/**
* @author: guorui fu
* @versiion: 1.0
* 克鲁斯卡算法
*/
public class KruskalCase {
private int edgeNum;//边的个数
private char[] vertexes;//顶点数组
private int[][] matrix;//邻接矩阵
private static final int INF = Integer.MAX_VALUE;//用来表示顶点不能连通
public static void main(String[] args) {
char[] vertexes = {'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},
};
//创建对象实例
KruskalCase kruskalCase = new KruskalCase(vertexes, matrix);
kruskalCase.print();
//拿到所有图中所有边
// EData[] edges = kruskalCase.getEdges();
// System.out.println("排序前" + Arrays.toString(edges));
// kruskalCase.sortEdges(edges);
// System.out.println("排序后" + Arrays.toString(edges));
kruskalCase.kruskal();
}
//构造器
public KruskalCase(char[] vertexes,int[][] matrix){
int vlen = vertexes.length;//顶点数
//初始化顶点。复制拷贝
this.vertexes = new char[vlen];
for (int i = 0; i < vertexes.length; i++) {
this.vertexes[i] = vertexes[i];
}
// this.vertexes = vertexes;
//初始化边 这次直接克隆 或者可以双层循环进行复制拷贝
this.matrix = matrix.clone();
//统计边
for (int i = 0; i < vlen; i++) {
for (int j = 0; j < vlen; j++) {
if (this.matrix[i][j] != INF && this.matrix[i][j] != 0){
edgeNum++;
}
}
}
}
//
public void kruskal(){
int index = 0;//表示最后结果数组的索引
//保存已有最小生成树的每个顶点的终点 也就是遍历到相同顶点时终点只能是之前保存过顶点的终点
int[] ends = new int[edgeNum];
//创建EData数组,保存最小生成树结果
EData[] rets = new EData[edgeNum];
//获取图 所有边的集合,一共有12条
EData[] edges = getEdges();
//按变得权值进行从小到大的排序 从权值小到大遍历
sortEdges(edges);
//遍历edges 判断是否构成回路
for (int i = 0;i < edgeNum/2;i++){//已经排序过,从数组中最小权值的边开始
//获取到第i条边的第一个顶 和 第二个顶点
int p1 = getPosition(edges[i].start);
int p2 = getPosition(edges[i].end);
//获取p1 p2顶点在已有的最小生成树的终点是
int e1 = getEnd(ends, p1);
int e2 = getEnd(ends, p2);
//是否构成回路 也就是终点不能相同才可以加入最小树
if (e1 != e2){//ends[e1] 下标e1代表边起点 值e2代表边终点
ends[e1] = e2;//e1的终点是e2
rets[index++] = edges[i];//有一条边加入rets数组
}
}
//打印最小生成树 输出rets
System.out.println("最小生成树为=" + Arrays.toString(rets));
}
//打印邻接矩阵
public void print(){
System.out.println("邻接矩阵为:");
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.printf("%12d",matrix[i][j]);
}
System.out.println();
}
}
//对边的集合按权值大小进行排序处理 冒泡排序
//完成排序后 就可以从权值最小的顶点出发 进行连接
private void sortEdges(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;
}
}
}
}
//传入顶点值,返回下标
private int getPosition(char ch){
for (int i = 0; i < vertexes.length; i++) {
if (ch == vertexes[i]){
return i;
}
}
return -1;
}
//获取图中的边 放入EData[]数组中,之后需要遍历该数组
private EData[] getEdges(){
int index = 0;
EData[] edges = new EData[edgeNum/2];
for (int i = 0; i < vertexes.length; i++) {//斜对称 坐标ij与ji边相同,所以遍历其中一半部分就行
for (int j = i + 1; j < vertexes.length; j++) {
if (matrix[i][j] != INF){
//保存边的起点 终点 权值
edges[index++] = new EData(vertexes[i],vertexes[j], matrix[i][j]);
}
}
}
//返回所有边的信息集合:起点 终点 权值
return edges;
}
//获取下标为i的顶点的终点,用于判断两个顶点的终点是否相同
//ends[] 是在遍历过程中逐步形成 i表示传入的顶点对应的下标
private int getEnd(int[] ends,int i){
while (ends[i] != 0){//循环找到终点 例如ends[1]=3 -> ends[3]=6 ->ends[6]=0 则返回终点6
i = ends[i];
}
return i;
}
}
//创建一个类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 +
'}';
}
}
[算法] 克鲁卡尔算法 最小生成树
最新推荐文章于 2024-09-22 11:42:19 发布