克鲁斯卡尔算法介绍
1)克鲁斯卡尔(Kruskal)算法, 是用来求加权连通图的最小生成树的算法。
2)基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回
路
3) 具体做法:首先构造一 个只含n个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止
克鲁斯卡尔算法与普利姆算法都是求最小生成树
而普利姆算法是任意顶点开始 选择权值最小的路径进行连通
克鲁斯卡尔算法是优先连通权值最小的路径
import java.util.Arrays;
public class KeuskalCase {
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 } };
//创建对象
KeuskalCase keuskalCase = new KeuskalCase(vertexs,matrix);
keuskalCase.print();
EData[] edges = keuskalCase.getEdges();
//System.out.println(Arrays.toString(edges));
keuskalCase.sortEdges(edges);
//System.out.println(Arrays.toString(edges));
keuskalCase.kruskal();
}
//构造器
public KeuskalCase(char[] vertexs,int[][] matrix) {
//初始化顶点数
int vlen = vertexs.length;
//初始化顶点 复制拷贝的方式
this.vertexs = new char[vlen];
for(int i = 0;i < vertexs.length;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] ;
}
}
//统计边
for(int i = 0;i < vlen;i++){
for(int j= i+1;j < vlen; j++) {
if(this.matrix[i][j] != INF) {
edgeNum++;
}
}
}
}
//打印邻接矩阵
public void print() {
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();
}
}
//对边的权值进行排序(冒泡)
public 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 tmp = edges[j];
edges[j] = edges[j+1];
edges[j+1] = tmp;
}
}
}
}
private int getPosition(char ch) {
//ch顶点的值 返回该顶点下标
for(int i = 0;i < vertexs.length;i++) {
if(vertexs[i] == ch) {
return i;
}
}
//找不到返回 -1
return -1;
}
//获得边的对象数组('a','v','3')()()...
private EData[] getEdges() {
//获取图中的边 放入数组
int index = 0;
EData[] edges = new EData[edgeNum];
for(int i = 0;i < vertexs.length;i++) {
for(int j = i+1; j < vertexs.length;j++) {
if(matrix[i][j] != INF) {
edges[index++] = new EData(vertexs[i],vertexs[j],matrix[i][j]);
}
}
}
return edges;
}
//获取下标i的点的终点
//ends[] 记录各个顶点对应的终点是哪个 是在遍历过程中逐步形成的
//i 传入的顶点对应的下标
private int getEnd(int[] ends,int i) {
if(ends[i] != 0) {
i = ends[i];
}
return i;
}
public void kruskal() {
int index = 0;//表示最后结果数组的索引
int[] ends = new int[edgeNum];//用于保存已有最小生成树中的每个顶点的终点
//创建结果数组 保存最后的最小生成树
EData[] rets = new EData[edgeNum-1];
//获取图中所有边的集合
EData[] edges = getEdges();
System.out.println(Arrays.toString(edges));
sortEdges(edges);//对边排序
//遍历edges数组,将边添加到最小生成树中时,判断是准备加入的边否形成了回路,如果没有,就加入rets,否则不能加入
for(int i = 0;i < edgeNum;i++) {
//遍历边
int p1 = getPosition(edges[i].start);//得到边的一点的下标
int p2 = getPosition(edges[i].end);//得到边的另一点的下标
//获取p1 p2 的终点
int m = getEnd(ends,p1);
int n = getEnd(ends,p2);
//判断是否构成回路
if(m != n) {
//未构成回路
ends[m] = n;//设置m的终点为n
rets[index++] = edges[i];//加入
}
}
//统计并打印rets
System.out.println("最小生成书" + Arrays.toString(rets));
}
}
//创建一个类 每个对象代表一条边
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;
}
public String toString() {
return "EData [start=" + start + ", end=" + end + ", weight=" + weight + "]";
}
}