克鲁斯卡尔算法
- 克鲁斯卡尔算法,是用来求加权连通图的最小生成树的算法
- 基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路
- 具体做法:首先构造一只含有n个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林不构成回路,直到森林变成一棵树为止
应用场景
- 某城市新增7个站点(A, B, C, D, E, F, G),现在需要修路把7个站点连通
- 各个站点的距离用边线表示(权),比如A-B距离为12公里
- 问:如何修路保证各个站点都能连通,并且修建的总公里数最短?
代码实现
public class KrushkalCase {
private int edgeNum; //边的个数
private char[] vertex; //顶点数组
private int[][] matrix; //邻接矩阵
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 = {
{ 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}
};
KrushkalCase krushkalCase = new KrushkalCase(vertexs, matrix);
krushkalCase.kruskal();
}
public void kruskal() {
int[] ends = new int[edgeNum]; //用于保存已有最小生成树的中的每个顶点的在最小生成树中的终点
//创建结果数组
EData[] rests = new EData[edgeNum];
//获取图中的边
EData[] edges = getEdges();
sortEdges(edges);
//遍历edges数组,将边添加到最小生成树的时候,判断是否构成回路,未构成,添加
for (int i = 0; i < edgeNum; i++) {
//获取到第i条边的第一个顶点
int p1 = getPosition(edges[i].start);
int p2 = getPosition(edges[i].end);
//获取p1的顶点在已有的最小生成树中的终点
int m = getEnd(ends, p1);
//获取p2的顶点在已有的最小生成树中的终点
int n = getEnd(ends, p2);
//是否构成回路
if (m != n) { //没有构成回路
ends[m] = n; //设置m在已有最小生成树的终点
rests[i] = edges[i];
System.out.println(edges[i]);
}
}
}
public KrushkalCase(char[] vertexs, int matrix[][]) {
//初始化顶点数和边的个数
int vlen = vertexs.length;
//初始化顶点
this.vertex = new char[vlen];
for (int i = 0; i < vlen; i++) {
this.vertex[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++;
}
}
}
//打印邻接矩阵
print();
}
//打印邻接矩阵
public void print() {
System.out.println("邻接矩阵为:");
for (int i = 0; i < vertex.length; i++) {
for (int j = 0; j < vertex.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 - i - 1; j++) {
if (edges[j].weight > edges[j+1].weight) {
EData temp = edges[j];
edges[j] = edges[j+1];
edges[j+1] = temp;
}
}
}
}
//返回ch顶点对应的下标,找不到返回-1
private int getPosition(char ch) {
for (int i = 0; i < vertex.length; i++) {
if (ch == vertex[i]) {
return i;
}
}
return -1;
}
//获取图中的边,放入Edata数组
private EData[] getEdges() {
EData[] edges = new EData[edgeNum];
int index = 0;
for (int i = 0; i < vertex.length; i++) {
for (int j = i + 1; j < vertex.length; j++) {
if (matrix[i][j] != INF) {
edges[index++] = new EData(vertex[i], vertex[j], matrix[i][j]);
}
}
}
return edges;
}
//获取下标为i的顶点的终点
private int getEnd(int ends[], int i) {
while (ends[i] != 0) {
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 "EDate [start=" + start + ", end=" + end + ", weight=" + weight + "]";
}
}