并查集(union-set)

应用:union/set及其应用


并查集是一种树形的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题,常常在使用中以森林表示。

1. 等价关系

    具有自反性对称性传递性三个性质称为等价关系,如电气连通性。

    给定一个等价关系“~”,基于以上性质,为判断是否a~b,只需验证a和b是否同属一个等价类,这给我们提供了解决等价问题的方法。

2. 基本操作

   在初始化时使每个元素都属于一个集合,集合间不相交(disjoint)。

   find:  返回给定元素的集合(即等价类)的名字;

   union:使用求并操作添加关系,通常是先使用find判断二者是否已经有关系。

   解决动态等价问题的方案有两种:一种是保证指令find能够以O(1)运行;另一种是保证union能以O(1)运行。二者不可兼得。

3. 数据结构实现和分析

    3.1 find的O(1)可行性

    要保证find的O(1)性,union就必须多做一些工作。一种方案是在每次union时扫描待合并集合,将其类名j全换成目标集合类名i,这样连续N-1次union的复杂度为O(N*N);另一种想法是将每个等价类放入一个链表中,可以节省更新时间,但减少不了实际的渐进时间;还有一种是合并过程中,将较小类的名字改成较大的等价类名字,这样任意顺序的M次find和N-1次union最多花费O(M+NlogN)。

    3.2 union的O(1)可行性

    1)naive结构

          直接将root2的根改为root1,单次find最坏是O(N)

class DisjSets
{
	public:
		explicit DisjSets(int numElements);
		
		int find(int x) const;
		int find(int x);
		void unionSets(int root1, int root2);
	
	private:
		vector<int> s;
};

//初始化所有元素为树根,分别代表一个单独的等价类
DisjSets::DisjSet(int numElements): s(numElements){
	for (int i = 0; i < s.size(); i++ )
		s[i] = -1;
}

//rooti代表set_i的根,直接将rhs连到lhs的根上进行合并
void DisjSets::unionSets(int root1, int root2)
{
	s[roo2] = root1;
}

//递归查找元素x的树根(即所属集合)
int DisjSets::find(int x) const
{
	if (s[x] <0)
		return x;
	else 
		return find(s[x]);
}
    2)按大小求并(union by size)

       合并时总是使较小的树成为较大的树的子树,可以证明,任何节点的深度不会超过logN,单次find操作最坏为O(logN)。实现时通常让树根元素的值代表当前树的大小(树根都是负值)。

    3)按高度求并(union by height)

       合并时通过跟踪每棵树的高度(负值)使得浅树成为深度的子树,保证树的深度最多是O(logN)  。与按大小求并异曲同工。

//按高度求并
void DisjSets::unionSets(int root1, int root2)
{
	if (-s[root1] < -s[root2]) //root2深于root1
		s[root1] = root2;
	else
	{
		if (s[root1] == s[root2])
			s[root1]--; //root1与root2高度相等,总体高度加1
		s[root2] = root1;
	}
}
    4)find的路径压缩(path compression)

          基本上说,union操作的任何算法都会产生相同的最坏情形的树(即find的O(logN)。此时要降低整体的复杂度,就需要再次从find函数下手。

          一种方案是进行路径压缩,其效果是:每执行一次find函数,从x到树根上的每一个节点都使它的父节点变成树根。

int DisjSets::find(int x)
{
	if(s[x] < 0)
		return x;
	else
		return s[x] = find(s[x]);
}
         需要注意的是,路径压缩与按大小求并完全兼容(因为压缩后没有改变树的大小);但不与按高度求并兼容(因为改变了高度),由于无法准确计算压缩后数的高度,通常对于每棵树存储的高度都是估计值(称为“秩”),按秩求并理论上和按大小求并的效率是一样的。

         可以证明,使用按秩求并和路径压缩探测方法最坏情况下几乎是线性的,最坏情况下需要的时间是,实际中一般,一个较弱的结果是O(MlogN)。

4. 应用

    迷宫生成算法


《数据结构与算法分析 C++描述》原书第8章整理

你可能感兴趣的:(并查集(union-set))