并查集union find/disjoin set
并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。用于解决无向图中的环的问题。
(1)结构
定义每个节点的祖先节点是谁1
vector<int> parent;
(2)初始化
把每个点所在集合初始化为其自身。
通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N)。1
2
3
4
5
6
7
8
9
10
11union_find(int n)
{
parent=vector<int>(n);
init(n);
}
void init(int n)
{
for (int i=0;i<n;++i)
parent.at(i)=i;
}
(3)查找
查找元素所在的集合,即根节点(祖先节点)。1
2
3
4
5
6int find_root(int i)
{
while (i!=parent.at(i))
i=parent.at(i);
return i;
}
(4)合并
将两个元素所在的集合合并为一个集合。
通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的“查找”操作实现。1
2
3
4
5
6
7
8
9bool union_vertices(int a,int b)
{
int a_root=find_root(a);
int b_root=find_root(b);
if (a_root==b_root)
return false;
parent.at(a_root)=b_root;
return true;
}
(5)优化
路径压缩
每次查找的时候,如果路径较长,需要遍历较长的路径
实现:则修改信息,以便下次查找的时候速度更快。
第一步,找到根结点。
第二步,修改查找路径上的所有节点,将它们都指向根结点。1
2
3
4
5
6
7
8
9
10int find_root(int i)
{
if (parent.at(i)!=i)
{
int root=find_root(parent.at(i));
parent.at(i)=root;
return root;
}
return i;
}最大秩合并
两个集合合并时候,两个集合合并可能导致深度进一步增大,导致查找根节点时遍历路径过长。
实现:就是在对两个不同子集连接时,按照深度来连,也就是深度低的连在深度高的下面。深度高的做父亲节点。
但是因为会结合路径压缩同时优化,所以rank数组不一定代表的是深度,但也是一个优化的衡量标准。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34vector<int> parent;
vector<int> rank;
union_find(int n)
{
parent=vector<int>(n);
rank=vector<int>(n,0);
init(n);
}
void init(int n)
{
for (int i=0;i<n;++i)
{
parent.at(i)=i;
rank.at(i)=0;
}
}
bool union_vertices(int a,int b)
{
int a_root=find_root(a);
int b_root=find_root(b);
if (a_root==b_root)
return false;
if (rank.at(a_root)<rank.at(b_root))
parent.at(a_root)=b_root;
else
{
parent.at(b_root)=a_root;
if (rank.at(a_root)==rank.at(b_root))
++rank.at(a_root);
}
return true;
}
(6)板子
union_find.h1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
using namespace std;
class union_find
{
private:
vector<int> parent;
vector<int> rank;
public:
union_find()
{
parent.clear();
rank.clear();
}
union_find(int n)
{
parent=vector<int>(n);
rank=vector<int>(n,0);
init(n);
}
void init(int n)
{
for (int i=0;i<n;++i)
{
parent.at(i)=i;
rank.at(i)=0;
}
}
int find_root(int i)
{
if (parent.at(i)!=i)
{
int root=find_root(parent.at(i));
parent.at(i)=root;
return root;
}
return i;
}
bool union_vertices(int a,int b)
{
int a_root=find_root(a);
int b_root=find_root(b);
if (a_root==b_root)
return false;
if (rank.at(a_root)<rank.at(b_root))
parent.at(a_root)=b_root;
else
{
parent.at(b_root)=a_root;
if (rank.at(a_root)==rank.at(b_root))
++rank.at(a_root);
}
return true;
}
};