前言
上一篇主要介绍了求解最小生成树算法之一的Prim算法。本篇主要介绍的是另一种Kruskal算法。相比于Prim算法,kruskal算法更易理解。
Kruskal算法流程
以下面这个图为例:

首先将一个图中所有边按照权值大小进行排列,来判断是否所有的边边是否应该加入到集合中。

接下来是一个回填的过程,在列表中按次序依次取出边回填到图中。每回填一条新边都要判断图中是否存在环,如果存在环则跳过这条边。
这是当前的全部集合,此时总集合边的数量为4。

此时我们可以看到2-5这条边的值最小,因为2所在的集合与5所在的集合不同,所以可以连接,边数加1。

而在进行下一条边操作时,当6-8这条边加入时会形成环,因此这条边被舍弃,开始判断下一条边。

重复上述过程直到已经选择了 n − 1 n-1 n−1条边, n n n为顶点的个数,kruskal算法完成。

Kruskal算法应用实例
题目描述
给定一个
n
n
n
个点
m
m
m
条边的无向图,图中可能存在重边和自环,边权可能为负数。
求最小生成树的树边权重之和,如果最小生成树不存在则输出 i m p o s s i b l e impossible impossible。
代码解释
解决该问题的代码
import java.io.*;
import java.util.*;
class Edge {
int a, b, c;
public Edge(int a, int b, int c) {
this.a = a;
this.b = b;
this.c = c;
}
}
class Main {
private static int N = 100010;
private static int M = 200010;
private static int[] p = new int[N];
private static int n, m;
private static int INF = 0x3f3f3f3f;
private static Edge[] g = new Edge[M];
public static int find(int x) {
//寻找x点的祖宗节点
if (p[x] != x) {
p[x] = find(p[x]);
}
return p[x];
}
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String[] str1 = bufferedReader.readLine().split(" ");
n = Integer.parseInt(str1[0]);
m = Integer.parseInt(str1[1]);
for (int i = 1; i <= n; i ++) {
p[i] = i;//初始化并查集
}
for (int i = 0; i < m; i ++) {
String[] str2 = bufferedReader.readLine().split(" ");
int a = Integer.parseInt(str2[0]);
int b = Integer.parseInt(str2[1]);
int c = Integer.parseInt(str2[2]);
g[i] = new Edge(a, b, c);
}
int t = kruskal();
if (t == INF) {
System.out.println("impossible");
} else {
System.out.println(t);
}
bufferedReader.close();
}
public static int kruskal() {
Arrays.sort(g, 0, m, new Comparator<Edge>(){
@Override
public int compare(Edge e1, Edge e2){
return e1.c - e2.c;
}
});
int res = 0;
int cnt = 0; //res记录最小生成树的树边权重之和,cnt记录的是全部加入到树的集合中边的数量(可能有多个集合)
for (int i = 0; i < m; i ++) {
Edge tmp = g[i];
int a = tmp.a;
int b = tmp.b;
int c = tmp.c;
a = find(a);
b = find(b);
if (a != b) {
/*
如果a和b已经在一个集合当中了,说明这两个点已经被一种方式连接起来了,
如果加入a-b这条边,会导致集合中有环的生成,而树中不允许有环生成,所以一个连通块中的点的数量假设
为x,那么里面x个节点应该是被串联起来的,有x-1条边,所以只有当a,b所属的集合不同时,才能将a-b这条
边加入到总集合当中去
*/
p[a] = b;
res += c;
cnt ++;
}
}
if (cnt < n - 1) {
// 边树小于n-1,不能构造最小生成树
return INF;
} else {
return res;
}
}
}
具体的算法流程也可以参照B站上的优质讲解视频:https://www.bilibili.com/video/BV1Eb41177d1?from=search&seid=3240486097514768944&spm_id_from=333.337.0.0
本文详细介绍了Kruskal算法的基本原理及其求解最小生成树的应用。通过实例演示了算法的具体步骤,并提供了完整的Java代码实现。

581

被折叠的 条评论
为什么被折叠?



