互质的集合 | Disjoint Set: Union Find Tree | C/C++实现

问题描述

请实现一个管理互质动态集合S = {S1, S2, …, Sk}的程序。
首先读取整数n,创建由0, 1, …, n-1这样n个互不相同的元素组成的集合。
然后读取整数q,对集合进行q个查询操作。查询包含以下2种:

unite(x, y):合并包含x的集合 S x S_x Sx与包含y的集合 S y S_y Sy
same(x, y):判断x与y是否包含于同一集合

输入:
n q
com1 x1 y1
com2 x2 y2

comq xq yq
第1行指定n和q。接下来q行指定查询操作。comi 代表查询的种类,“0”为 unite,“1”为 same。
输出: 各same查询中,x与y包含于同一集合时返回1,否则返回0,每个结果占1行。
限制:
1 ≤ n ≤ 10000
1 ≤ q ≤ 100000

输入示例

5 12
0 1 4
0 2 3
1 1 2
1 3 4
1 1 4
1 3 2
0 1 3
1 2 4
1 3 0
0 0 4
1 0 2
1 3 0

输出示例

0
0
1
1
1
0
1
1

讲解

Disjoint Sets 是一种用互质的集合(一个元素不同时包含于多个集合的集合)对数据进行分类管理的数据结构。这种数据结构可以有效地动态处理以下操作:

makeSet(x):创建仅包含元素x的新集合
findSet(x):求包含元素x的集合的“代表”元素
unite(x, y):合并指定的元素x、y

我们用findSet(x)可以找出元素x包含于哪个集合。再Disjoint Sets中,查询“指定的两个元素x、y是否包含于同一集合”的操作称为Union Find。

这里我们用名为Disjoint Sets Forests的森林结构来实现Disjoint Sets。本书中,我们将树的集合称为森林。构成森林的树代表各个集合,树的各结点代表集合内的各元素。

我们将各个树的根结点用作区分集合的代表元素。因此,findSet(x)将返回元素x所属树(集合)的根结点的值。为找出根结点,我们要让各结点具备指向其父结点的指针,以便从任意一结点都可以查询到根结点。另外,代表元素的指针要指向其自身。

findSet(x)的复杂度等于各结点到代表元素之间所经历的指针数,即树的高度。这里除了求代表元素之外,我们让findSet(x)再具备路径压缩的功能,从而提高后续findSet(x)的执行效率。所谓路径压缩,就是在求代表元素的同时,对起始元素到代表元素的路径上的所有结点进行修改,使结点的指针全部指向代表元素。

至于合并指定元素x、y的操作unite(x, y),我们要在x的代表元素和y的代表中选出一个作为新的代表元素,同时将另一个代表元素的指针指向新代表元素。

这里的关键问题是如何选出新代表元素。我们用表示各集合的树的高度作为判断依据。将以各结点x为根的树的高度计入变量rank[x]。初始状态下每个集合中都只有一个元素,因此rank[x]的值全部为0。

在合并集合时,如果两个集合的树的高度不同,则将较低的树合并至较高的树中(以防止新树比原树更高)。合并高度相同的树时,合并后新代表元素的rank增加1。

AC代码如下

#include
#include

using namespace std;

class DisjointSet{
	public:
		vector<int> rank, p;
		
		DisjointSet() {}
		
		DisjointSet(int size){
			rank.resize(size, 0);
			p.resize(size, 0);
			for(int i = 0; i < size; i++) makeSet(i);
		} 
		
		void makeSet(int x){
			p[x] = x;
			rank[x] = 0;
		}
		
		bool same(int x, int y){
			return findSet(x) == findSet(y);
		}
		
		void unite(int x, int y){
			link(findSet(x), findSet(y));
		}
		
		void link(int x, int y){
			if(rank[x] > rank[y]){
				p[y] = x;
			} else {
				p[x] = y;
				if(rank[x] == rank[y]){
					rank[y]++;
				}
			}
		}
		
		int findSet(int x){
			if(x != p[x]){
				p[x] = findSet(p[x]);
			}
			return p[x];
		}
}; 

int main(){
	int n, a, b, q;
	int t;
	
	cin>>n>>q;
	
	DisjointSet ds = DisjointSet(n);
	
	for(int i = 0; i < q; i++){
		cin>>t>>a>>b;
		if(t == 0) ds.unite(a, b);
		else if(t == 1){
			if(ds.same(a, b) ) cout<<1<<endl;
			else cout<<0<<endl;
		}
	}
	
	return 0;
}

你可能感兴趣的:(算法学习,数据结构)