赞
踩
并查集是一种树型的数据结构,主要适用于解决一些元素的分组问题。它管理一系列不相交的集合,并支持两种操作:
并查集的重要思想在于,用集合中的一个元素代表集合。从上面可以看出,最终得到的是一个树状结构,要寻找集合的元素的代表,只需要一层一层的往上访问父节点,直到找到父节点为自身的根节点。
如果共有 n n n个元素,我们可以使用一个数组来存储每个元素的父节点。其代码为:
int[] parent = new int[n];
for (int i = 0; i < len; i++) {
parent[i] = i;
}
对于并查集的查找操作,可以用递归的方法实现对代表元素的查询:一层一层的访问父节点,直至根节点。
int find(int v) {
return v == parent[v] ? v : find(parent[v]);
}
对于两个元素我们可以通过查找操作查找根节点,来判断两个元素是否属于同一个集合。
对于合并操作,可以先找到两个集合的代表元素,然后将前者的父节点设置为后者(当然也可以将后者的父节点设置为前者)。
void union(int i, int j) {
int x = find(i), y = find(j);
if (x == y) return;
parent[x] = y;
}
并查集的操作已经完成,但是并查集所形成的树可能是一条单链。对于这样的树越深,我们想要从底部找到根节点会变得越来越难。因此我们可以将一条单链转化为下面的形式:
上面的实现过程是:在查询的过程中,把沿途的每个节点的父节点都设为根节点即可。
int find(int v) {
return v == parent[v] ? v : (parent[v] = find(parent[v]));
}
由于上面的路径压缩只在查找元素根节点时起作用,所以加入路径压缩算法之后并查集也不始终都是只有两层树。
上面两种合并方法,如果可以选择的话我们更希望是后者(即使有路径压缩,但路径压缩也是会消耗时间的),因为后者的合并方法不会使树的深度加深。这样的话可以使用一个数组来记录每个树的深度,合并时比较两个根节点的深度,把深度较小者往较大者上合并。
路径压缩和按秩合并如果一起使用,时间复杂度接近 O ( n ) O(n) O(n) 。
int[] rank = new int[n]; int[] parent = new int[n]; for (int i = 0; i < len; i++) { parent[i] = i; rank[i] = 1; } void union(int i, int j) { int x = find(i), y = find(j); if (x == y) return; if (rank[x] <= rank[y]) { parent[x] = y; rank[y] += rank[x]; } else { parent[y] = x; rank[x] += rank[y]; } }
参考文献:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。