并查集(Union-Find)是解决动态连通性问题的一类非常高效的数据结构。
动态连通性
可以想象一张地图上有很多点,有些点之间是有道路相互联通的,而有些点则没有。如果我们现在要从点A走向点B,那么一个关键的问题就是判断我们能否从A走到B呢?换句话说,A和B是否是连通的。这是动态连通性最基本的诉求。现在给出一组数据,其中每个元素都是一对“点”,代表这对点之间是联通的,我们需要设计一个算法,让计算机依次读取这些数据,最后判断出其中任意两点是否连通。注意,并查集所涉及的动态连通性只是考虑“是否连通”这一二值判别问题,而不涉及连通的路径到底是什么。
举个例子,比如下图。为了简单起见,我们以整数 0~9 表示图中的10个点,然后给出两两连通的数据如下:[(4, 3), (3, 8), (6, 5), (9, 4), (2, 1), (8, 9), (5, 0), (7, 2), (6, 1), (6, 7)]
现在,“并”的操作可以这样来描述:观察第一个点对(4,3)(4,3),于是先找到点4和3,发现所在组别不一样,再将点4和3的组别都变成3(当然都变成4也行,这个随意设计),然后就产生了如下的表:
element | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
group number | 0 | 1 | 2 | 3 | 3 | 5 | 6 | 7 | 8 | 9 |
推荐一个写的很不错的博客:https://blog.csdn.net/Hacker_ZhiDian/article/details/60965556
为了方便自己以后复习,参考这个博客自己写了一个Java版的代码
public class bingchaji {
static int N=10;
static int[] f=new int[N];
static int [] high=new int[N];
public static void main(String[] args) {
int [][] m=new int[4][2] ;
init();
merge(1,3);
merge(2,4);
merge(3,4);
merge(1,4);
merge(5,6);
for(int num:f) {
System.out.println(num);
}
}
//将所有点的“祖先”都设置为自己的 id ,
static void init() {
for(int i=1;i<N;i++) {
f[i]=i;
}
}
static int getFriend(int v) {
if(f[v]==v) {
return v;
}
/*
* 如果发现“祖先”不是自己,那么肯定被合并到别的圈里面去了,
* 那么继续调用这个函数来找这个圈里面的“祖先”,
* 并且在这个过程中将找到的点都设置为同一个“祖先”(因为都在同一个圈里面)
*/
return f[v]=getFriend(f[v]);
}
/**
* 将两个数所在的两个圈合并为一个圈
* 这里通过两个圈的高度来决定合并方式
* @param a
* @param b
*/
static void merge(int a,int b) {
int t1=getFriend(a);
int t2 = getFriend(b);
// 两个点的“祖先”不一样,合并两个圈
if(t1!=t2) {
// 如果左边的圈的高度大于右边的圈的高度,
// 那么将右边的圈合并到左边的圈中
if(high[t1]>high[t2]) {
f[t2]=t1;
}else {
f[t1]=t2;
// 如果当前两个圈的高度相等,那么合并之后的圈高度要加一
if(high[t1]==high[t2]) {
high[t2]++;
}
}
}
}
}