并查集是什么
并查集是一种用来管理元素分组的数据结构,可以高效进行合并查找操作
- 查询元素a和元素b是否是同一分组
- 合并元素a和元素b所在分组
并查集的结构
初始化
准备n个节点表示n个元素,最开始每个节点都是单独的一个集合
合并
从一个组的根向另一个组的根连边
查询
为了查询两个节点是否属于同一组,我们需要沿着树网上找,来查询根是谁,如果两个节点根一样,则属于同一组
元素1,2都网上找到根为3,属于同一组,元素4,6都找到根为5,属于同一组。
并查集优化
-
记录树的高度rank,合并的时候由rank小的向rank大的连边
-
路径压缩,一旦某个节点向上走到了根节点,就把这个节点的父亲直接指向根节点
并查集实现
public class Union {
int[] par;
int[] rank;
public Union(int size){
assert size>0;
par = new int[size];
rank = new int[size];
for(int i=0;i<size;i++){
par[i] = i;
rank[i] = 1;
}
}
public Map<Integer,List<Integer>> allUnionSet(){
findAll();
//key是par数组的值,value是下标集合
Map<Integer,List<Integer>> map = new ConcurrentHashMap<>();
for(int i=0;i<par.length;i++){
int finalI = i;
map.computeIfAbsent(par[i], k -> new ArrayList<>()).add(finalI);
}
return map;
}
//让所有节点直连根节点
private void findAll(){
for(int i=0;i<par.length;i++){
find(i);
}
}
//查询树根
public int find(int x){
if(x < 0 || x >= par.length)
throw new IllegalArgumentException("p is out of bound.");
if(par[x] == x){
return x;
}else{
//路径压缩
return par[x] = find(par[x]);
}
}
//合并x和y所属集合
public void union(int x,int y){
x = find(x);
y = find(y);
if(x==y)
return;
//rank小的向大的连
if(rank[x]<rank[y]){
par[x] = y;
}else{
par[y] = x;
if(rank[x]==rank[y]){
rank[x]++;
}
}
}
public boolean same(int x,int y){
return find(x) == find(y);
}
public static void main(String[] args) {
Union union = new Union(4);
System.out.println(union.allUnionSet());
union.union(0,1);
System.out.println(union.allUnionSet());
union.union(1,2);
System.out.println(union.allUnionSet());
}
}
console:
{0=[0], 1=[1], 2=[2], 3=[3]}
{0=[0, 1], 2=[2], 3=[3]}
{0=[0, 1, 2], 3=[3]}