并查集(Union-Find)算法

并查集(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]++;
				}
			}
		}
	}
	
	
}

猜你喜欢

转载自blog.csdn.net/Broken_Wave/article/details/82081731