最小生成树 - Kruskal算法
一、题目描述:
给出一个无向图,求出最小生成树,如果该图不连通,则输出
orz
。
输入格式
第一行包含两个整数 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条边,最小生成树也就完成了