最小生成树 - Kruskal算法
一、题目描述:
给出一个无向图,求出最小生成树,如果该图不连通,则输出
orz
。
来源:洛谷 www.luogu.com.cn/problem/P33…
输入格式
第一行包含两个整数 N,MN,M,表示该图共有 N 个结点和 M 条无向边。
接下来 M 行每行包含三个整数 Xi,Yi,Zi,表示有一条长度为 Zi 的无向边连接结点 Xi,Yi。
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
输出格式
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz
。
7
二、思路分析:
-
什么是最小生成树? 就是将图中的顶点全部连接起来(任意两个顶点能够连接),使连接起来的边权值最小。这里注意连接起来的顶点不可能成环(没有意义嘛),因此n个顶点连接,边的个数应该是n-1
-
Kruskal? Kruskal(克鲁斯卡尔)算法是最小生成树是经典算法之一,基于贪婪算法的思想,优先选择权值最小的边连接两个顶点,针对边,对于稀疏图(边数少)时更好。 但是连接的时候我们需要进行判断,判断这个两个顶点是否已经通过其他顶点连接,如果它们已经连接就丢弃(没有意义嘛)。如果说这两个顶点没有连接,那么就把它们两个连接起来,把与这两个顶点相关的顶点也连接(把它们记录下来),这里使用到了并查集记录。
三、AC 代码:
import java.util.*;
public class Main {
// f[] 记录这个顶点的集合头头(判断两个顶点是否连接)
static int f[];
static int n;
public static void main(String[] args) {
Scanner sr = new Scanner(System.in);
n = sr.nextInt(); //结点
int m = sr.nextInt(); //无向边
f = new int[n + 1];
// 创建大小是m的Node 数组(刚好会比较好,不然不好排序)
Node node[] = new Node[m];
// 初始化,每个顶点的祖先都是自己
for (int i = 1; i <= n; i++)
f[i] = i;
for (int i = 0; i < m; i++) {
int x = sr.nextInt();
int y = sr.nextInt();
int z = sr.nextInt();
node[i] = new Node(x, y, z);
}
// 排序,根据边的权值从小到大排列
Arrays.sort(node, new Comparator<Node>() {
@Override
public int compare(Node o1, Node o2) {
return o1.weight - o2.weight;
}
});
int count = 0, ans = 0;
for (Node temp : node) {
// 边的个数是n-1 说明n个顶点连接起来了,就可以直接输出结果了
if (count == n - 1) break;
int xx = find(temp.from);
int yy = find(temp.to);
// 如果它们的祖先不一样, 说明from顶点 和 to顶点没有连接
if ( xx != yy ) {
f[xx] = yy;
ans += temp.weight;
count++;
}
}
if (count == n - 1) {
System.out.println(ans);
} else System.out.println("orz");
}
//找祖先并统一祖先
static int find(int x) {
if (x == f[x]) return x;
return f[x] = find(f[x]);
}
static class Node {
int from, to, weight;
Node(int from, int to, int w) {
this.from = from;
this.to = to;
this.weight = w;
}
}
}
四、总结:
Kruskal写最小生成树还是很好理解的,主要就是对边从小到大进行排序,依次取,看看对现在的图有没有用,有用就放上去,并改变能通过这条边进行连接的顶点,当放上去n-1条边,最小生成树也就完成了
转载自:https://juejin.cn/post/7075134583758389261