归子莫的博客

「笔杆揭不起,绘不出青烟别春泥 ————归子莫」

常用十大算法(七)— 克鲁斯卡尔算法

博客说明

文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢!

介绍

  • 克鲁斯卡尔(Kruskal)算法,是用来求加权连通图的最小生成树的算法。

最小生成树

  • 最小生成树(Minimum Cost Spanning Tree),简称MST。
  • 给定一个带权的无向连通图,如何选取一棵生成树,使树上所有边上权的总和为最小,这叫最小生成树
  • N个顶点,一定有N-1条边
  • 包含全部顶点
  • N-1条边都在图中
  • 求最小生成树的算法主要是普里姆 算法和克鲁斯卡尔算法

修路问题

image-20200906121921500

  • 有北京有新增7个站点(A, B, C, D, E, F, G) ,现在需要修路把7个站点连通
  • 各个站点的距离用边线表示(权) ,比如 A – B 距离 12公里
  • 问:如何修路保证各个站点都能连通,并且总的修建公路总里程最短?

思路

  • 基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路
  • 具体做法:首先构造一个只含n个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止
问题一

排序

问题二

判断回路

代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package com.atguigu.kruskal;

import java.util.Arrays;

public class KruskalCase {

private int edgeNum; //边的个数
private char[] vertexs; //顶点数组
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[][] = {
/*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}
};
KruskalCase kruskalCase = new KruskalCase(vertexs, matrix);
kruskalCase.print();
kruskalCase.kruskal();
}

//构造器
public KruskalCase(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 kruskal() {
int index = 0;
int[] ends = new int[edgeNum];
EData[] rets = new EData[edgeNum];

EData[] edges = getEdges();
System.out.println("图的边的集合=" + Arrays.toString(edges) + "共"+ edges.length); //12

//排序
sortEdges(edges);

//遍历数组
for(int i=0; i < edgeNum; 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;
rets[index++] = edges[i];
}
}
System.out.println("×îСÉú³ÉÊ÷Ϊ");
for(int i = 0; i < index; i++) {
System.out.println(rets[i]);
}
}


public void print() {
System.out.println("邻接矩阵: \n");
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();
}
}

//对边排序
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 tmp = edges[j];
edges[j] = edges[j+1];
edges[j+1] = tmp;
}
}
}
}

//顶点的值
private int getPosition(char ch) {
for(int i = 0; i < vertexs.length; i++) {
if(vertexs[i] == ch) {
return i;
}
}
return -1;
}

//获取图中的边
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;
}

//获取终点
private int getEnd(int[] ends, int i) { ]
while(ends[i] != 0) {
i = ends[i];
}
return 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 + ", " + end + ">= " + weight + "]";
}
}

感谢

尚硅谷

以及勤劳的自己,个人博客GitHub

微信公众号

评论