并查集原理及实现

原理

将图中所有相连的点都指向同一个点,代表这些点都属于同一个集合

实现

需要一个fa[]数组来记录每个点所指向的点,所有点初始都指向自己(此处可以理解为fa[]用来指向自己的父亲,初始为自己指向自己,虽然我就是我自己的父亲听起来有点怪怪的)

初始化

for(int i = 0; i <= n; i++){
    fa[i] = i;
}

并查集(模板)

int Find(int x){
    return fa[x] = fa[x] == x ? x : Find(fa[x]);
    //如果自己指向自己,说明此时已经为该集合的祖先,则返回自己
    //如果自己不指向自己,说明自己不是该集合的祖先,则继续寻找该集合的祖先,并将自己的父节点变为变为该集合的祖先
}
​
void Union(int x, int y){
    fa[fd(x)] = fd(y);
    //将x的祖先更新为y的祖先
}

上面这两个函数就是并查集的模板(确实只有两句话)

注意事项

我们每次查找两个点是否属于同一个集合的时候,一定要用下面的方法:

int x, y;
//判断x点和y点是否属于同一祖先
fd(x) == fd(y)

下面这个是错误示例:

//错误示例
fa[x] == fa[y]
//错误示例

为什么会有这个错误呢?这里举个例子,顺序合并点

1 2
1 3
3 4

 

此时我们可以发现,当只链接3和4的时候,因为3已经是一个集合的祖先了,但是它的孩儿们依旧指向他们的祖先,即点3,此时有

fa[1] = 3;
fa[2] = 3;
fa[3] = 4;
fa[4] = 4;

所以此时如果用了错误示例来查找13是否是同一个祖先的时候会发现fa[1] == 3但是fa[3] == 4,因此会判断不在同一个集合中,但是如果是fa[Find(1)]进行查找的时候,则会对点1的祖先进行更新,此时有fa[1] == 4.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值