并查集原理
并查集本质上是树的父亲表示法,用一个数组去保存当前结点的父结点(如果当前元素是根,那么数组中会存放元素本身)。并查集用于处理一些不相交集合的合并及查询问题(即所谓的并、查)。比如说,我们可以用并查集来判断一个森林中有几棵树、某个节点是否属于某棵树等。
下面是Java代码的一个简单实现:
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
| import java.util.Arrays;
public class FindSet { private int[] pre; private int[] rank; private int size; public FindSet(int size) { this.size = size; pre = new int[size]; for (int i = 0; i < size; i++) { pre[i] = i; } rank = new int[size]; Arrays.fill(rank, 1); }
public FindSet() { this(256); }
public int find(int x) { if (pre[x] == x) { return x; } return pre[x] = find(pre[x]); }
public boolean isSame(int x, int y) { return find(x) == find(y); }
public boolean union(int x,int y) { x = find(x); y = find(y); if(x == y) return false; if(rank[x] > rank[y]) pre[y] = x; else { if(rank[x] == rank[y]) rank[y]++; pre[x] = y; } return true; }
}
|
用Kruskal去解决最小生成树
Kruskal算法是处理边的,所以在稀疏的边比较少的连通网中,用Kruskal算法效率就比较高;在边比较多,点比较少的连通网中,用加点法Prim算法效率比较高
Kruskal算法:此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里,步骤如下:
- 把图中的所有边按权值从小到大排序,把图中的n个顶点看成独立的n颗树组成的森林
- 按权值从小到大选择边,所选的边连接的两个顶点$V_i$和 $V_j$应属于两颗不同的树,则称为最小生成树中的一条边,并合并成一棵树
- 重复步骤2,直到所有顶点都在一棵树内或者有n-1条边为止
例如,对于样例
1 2 3 4 5 6 7 8 9 10 11
| 10 A B 6 A C 1 A D 5 B C 5 C D 5 B E 3 C E 6 C F 4 E F 6 D F 2
|
预期结果:
边的定义如下:
1 2 3 4 5
| public class Edge { public char start; public char end; public int w; }
|
Kruskal算法如下:
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
| public class Main { public static void main(String[] args) {
Scanner in = new Scanner(System.in); int n = in.nextInt(); Edge[] edges = new Edge[n]; FindSet findSet = new FindSet(); for (int i = 0; i < n; i++) { edges[i] = new Edge(); edges[i].start = in.next().charAt(0); edges[i].end = in.next().charAt(0); edges[i].w = in.nextInt(); } Arrays.sort(edges, (o1, o2) -> o1.w - o2.w); for (Edge e : edges) { int root1 = findSet.find(e.start); int root2 = findSet.find(e.end); if (root1 != root2) { findSet.union(root1, root2); System.out.println(e.start + " " + e.end + " " + e.w); } }
} }
|
程序运行结果: