并查集是一种树状的数据结构,其特点是各元素间可连接不同元素或都连接同一元素,常用于图的连通分量的查找。
英文。。。DisjointSet,UnionFind?随你怎么叫吧!
实现(无向图) union的三种实现方法 quickFind并查集最简单的实现,查找直接返回结点的上一级,每次查找只需要访问一次数组,缺点是每次union操作都需要遍历数组去查找参数的根节点,最坏情况达到平方级别,只适用于小规模的问题
【关于复杂度的问题以后有兴趣再深入探究】
public class QuickFindUF
{
private int[] id; // 分量id(以触点作为索引)
private int count; // 分量数量
public QuickFindUF(int N)
{ // 初始化分量id数组
count = N;
id = new int[N];
for (int i = 0; i < N; i++)
id[i] = i;
}
public int count() {
return count;
}
public boolean connected(int p, int q) {
return find(p) == find(q);
}
public int find(int p){
return id[p];
};
public void union(int p, int q){
int pId = find(p);
int qId = find(q);
};
[图示]
quickUnion对find的算法做了改进,使得find能逐链接索引直到根节点,union时只需要连接两个根节点即可,但是没能解决递归树倾斜问题,最坏输入情况时并查集将沦为链表,届时查找时间复杂度为O( N 2 N^2 N2)
【关于复杂度的问题以后有兴趣再深入探究】
package Chapter1.UnionFind;
public class QuickUnionUF {
//由只索引上级改为链式索引到根
public int find(int p){
while(id[p] != p)
p = id[p];
return id[p];
};
//直接连接根节点,不判断两个根树各自的结点数,易造成树倾斜
public void union(int p, int q){
int pRoot = find(p);
int qRoot = find(q);
if(pRoot == qRoot) return;
id[pRoot] = qRoot;
count--;
};
}
加权quickUnion(按秩压缩)
将结点少的根树连接到结点多的根树下,有效防止了树倾斜
public class WeightedQuickUnionUF {
private int[] id; // 分量id(以触点作为索引)
private int count; // 分量数量
private int[] sz; // 结点数量
public WeightedQuickUnionUF(int N)
{ // 初始化分量id数组
count = N;
id = new int[N];
for (int i = 0; i < N; i++)
id[i] = i;
sz = new int[N];
for(int i = 0; i < N; i++)
sz[i] = 1;
}
public int count() {
return count;
}
public boolean connected(int p, int q) {
return find(p) == find(q);
}
public int find(int p){
while(id[p] != p) {
p = id[p];
}
return id[p];
};
public void union(int p, int q){
int pRoot = find(p);
int qRoot = find(q);
if(pRoot == qRoot) return;
// 结点数小的数连到结点大的树下
if(sz[pRoot] > sz[qRoot]){
id[qRoot] = id[pRoot];
sz[pRoot] += sz[qRoot];
}
else{
id[pRoot] = id[qRoot];
sz[qRoot] += sz[pRoot];
}
count--;
};
}
优化
路径压缩
会将查询路径上遇到的所有节点都连接到根节点上,使得查询效率接近常数
public class PathHalvingUF {
private int[] id; // 分量id(以触点作为索引)
private int count; // 分量数量
private int[] sz; // 结点数量
public PathHalvingUF(int N)
{ // 初始化分量id数组
count = N;
id = new int[N];
for (int i = 0; i < N; i++)
id[i] = i;
sz = new int[N];
for(int i = 0; i < N; i++)
sz[i] = 1;
}
public int count() {
return count;
}
public boolean connected(int p, int q) {
return find(p) == find(q);
}
public int find(int p){
int root = p;
while(id[root] != root) {
root = id[root];
}
//查找到根节点后,从所查节点向上索引将结点一个个改连到根节点上
while(p != root){
int orgFather = id[p];
id[p] = root;
p = orgFather;
}
return root;
};
public void union(int p, int q){
int pRoot = find(p);
int qRoot = find(q);
if(pRoot == qRoot) return;
// 结点数小的数连到结点大的树下
if(sz[pRoot] > sz[qRoot]){
id[qRoot] = id[pRoot];
sz[pRoot] += sz[qRoot];
}
else{
id[pRoot] = id[qRoot];
sz[qRoot] += sz[pRoot];
}
count--;
};
性能一览
扩展参考
《算法4》 1.5



