用法:
主要用于解决一些元素分组的问题。它管理一系列不相交的集合,并支持两种操作:
- 合并(Union):把两个不相交的集合合并为一个集合。
- 查询(Find):查询两个元素是否在同一个集合中。
结构和操作实现:
数组实现,b[i]表示第i个元素的父节点,将1-n个元素看作n个不同集合。
初始化状态:
初始状态每个集合只有自己本身。箭头指向这个元素的父节点(父节点为自己本身)。
代码实现初始化:数组下标表示元素,内容表示父节点,开始指向自己;
合并集合:
如1和3两个集合合并,需要找到两集合的祖先(父节点为自己本身的祖先节点)。
2和1两个集合合并,1这个集合有1,2,3个元素;同理,合并完后如右下图。
查询集合:
若我们需要找3这元素属于哪个集合,需要对其父节点进行递归找到祖先。
当b[x] == x时,说明x元素为这个集合的祖先,他的子孙都属于x这集合。
当b[x] != x时,说明x元素不是祖先,需要对其父节点b[x]进行递归。
查询集合代码实现:
未压缩路径:
int getroot(int x)
{
if(b[x] == x) return x;
else return getroot(b[x]);
}
路径压缩:(更快,但破坏原来子和父的关系)
int getroot(int x)
{
if(b[x] == x) return x;
else return b[x] = getroot(b[x]);
}
合并代码实现:
图一1,3合并,需要找到1,3两个元素所属的集合(集合的祖先)。
合并过程:不在一个集合,将3祖先的父节点指向1的祖先,即b[3] = 3 ==> b[3] = 1;
void hebin(int x,int y)
{
int xp = getroot(x),yp=getroot(y);
if(xp != yp) b[xp] = yp;
}
例题:P1551 亲戚
https://www.luogu.com.cn/problem/P1551
思路:亲戚的亲戚也是亲戚,将有亲戚关系的集合都合并,只要两个人在同一个关系集合(祖先相同),两人为亲戚。