克鲁斯卡尔算法是求连通网的最小生成树的另一种方法。与普里姆算法不同,它的时间复杂度为O(eloge)(e为网中的边数),所以,适合于求边稀疏的网的最小生成树。基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路
1)有北京有新增7个站点(A,B,C, D,E, F, G),现在需要修路把7个站点连通
2)各个站点的距离用边线表示(权),比如A- B距离12公里
3)问:如何修路保证各个站点都能连通,并且总的修建公路总里程最短?
这个案例还是最小生成树的问题。
终点:将所有顶点按照从小到大的顺序排列好之后;某个顶点的终点就是"与它连通的最大顶点"。
上图中,将E-F、C-D、D-E加入最小生成树中后,这几条边的顶点的终点为:C>F,D>F,E>F,F>F,虽然第4步中C-E是最小的边,但是C和E的终点都是F,将C-E加入最小生成树中会构成回路。
public class KruskalDemo { 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, MAX_VALUE, MAX_VALUE, MAX_VALUE, 16, 14}, /*B*/ { 12, 0, 10, MAX_VALUE, MAX_VALUE, 7, MAX_VALUE}, /*C*/ { MAX_VALUE, 10, 0, 3, 5, 6, MAX_VALUE}, /*D*/ { MAX_VALUE, MAX_VALUE, 3, 0, 4, MAX_VALUE, MAX_VALUE}, /*E*/ { MAX_VALUE, MAX_VALUE, 5, 4, 0, 2, 8}, /*F*/ { 16, 7, 6, MAX_VALUE, 2, 0, 9}, /*G*/ { 14, MAX_VALUE, MAX_VALUE, MAX_VALUE, 8, 9, 0}}; KruskalDemo kruskal = new KruskalDemo(vertexs, matrix); kruskal.show(); kruskal.kruskal(); } private final char[] vertexs; //存放顶点 private int edgeNum; //边的个数 private final int[][] matrix; //邻接矩阵 private static final int MAX_VALUE = Integer.MAX_VALUE; /** * @param vertexs 存放顶点的数组 * @param matrix 邻接矩阵 */ public KruskalDemo(char[] vertexs, int[][] matrix) { int length = vertexs.length; this.vertexs = new char[length]; //给vertexs赋值 System.arraycopy(vertexs, 0, this.vertexs, 0, length); this.matrix = new int[length][length]; //给matrix赋值 for (int i = 0; i < length; i++) { System.arraycopy(matrix[i], 0, this.matrix[i], 0, length); } //统计边的数量 for (int i = 0; i < length; i++) { for (int j = i + 1; j < length; j++) { if (this.matrix[i][j] != MAX_VALUE) { edgeNum++; } } } } /** * 打印邻接矩阵 */ public void show() { 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(); } } /** * 获取图中的所有边 * @return 存储图中边的数组 */ private EData[] getEdges() { EData[] eData = new EData[edgeNum]; int index = 0; for (int i = 0; i < vertexs.length; i++) { for (int j = i + 1; j < vertexs.length; j++) { if (matrix[i][j] != MAX_VALUE) { eData[index++] = new EData(vertexs[i],vertexs[j],matrix[i][j]); } } } return eData; } /** * 获取顶点ch在数组中的下标 * @param ch 顶点的值 * @return 顶点在数组中的下标,找不到就返回-1 */ private int getPosition(char ch) { for (int i = 0; i < vertexs.length; i++) { if (vertexs[i] == ch) { return i; } } return -1; } /** * 冒泡排序 * @param eData 待排序的数组 */ private void sort(EData[] eData) { for (int i = 0; i < eData.length - 1; i++) { for (int j = 0; j < eData.length - 1; j++) { if (eData[j].weight > eData[j + 1].weight) { EData temp = eData[j]; eData[j] = eData[j + 1]; eData[j + 1] = temp; } } } } /** * 获取下标为i的顶点的终点 * @param ends 存储顶点对应的终点 * @param i 传入的顶点的下标 * @return i在数组中对应的顶点的终点的下标 */ private int getEnd(int[] ends,int i) { while (ends[i] != 0) { i = ends[i]; } return i; } /** * 克鲁斯卡尔算法 */ public void kruskal() { int index = 0; //结果数组的索引 int[] ends = new int[edgeNum]; //存放各个顶点对应的终点 EData[] results = new EData[edgeNum]; //存放结果 EData[] edges = getEdges(); //所有的边 sort(edges); for (int i = 0; i < edgeNum; i++) { int index1 = getPosition(edges[i].start); //第i条边的第一个顶点 int index2 = getPosition(edges[i].end); //第i条边的第二个顶点 int m = getEnd(ends, index1); //第i条边的第一个顶点的终点的下标 int n = getEnd(ends, index2); //第i条边的第二个顶点的终点的下标 if (m != n) { ends[m] = n; results[index++] = edges[i]; } } for (int i = 0; i < index; i++) { System.out.println(results[i]); } } } 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 + '}'; } }