【算法笔记】并查集

应用场景:

并查集可以非常快速的执行两个操作:

(1)将两个集合合并;
(2)询问两个元素是否在一个集合当中

基本原理:

用树的形式维护每个集合。每个集合的编号是根节点编号,每个点都存储了父节点是谁。当想求解某个点属于哪个集合的时候,一直找father找到树根。用这种方式快速找到元素属于哪个集合。每个节点存储他的父节点,p[x]表示x的父节点。

理解几个问题:

(1)如何判断树根?

如果是树根,有p[x] == x;如果不是树根,那么p[x] != x;

(2)如何求x的集合编号:

while (p[x] != x) x = p[x];只要x不是树根,就往上走。

(3)如何合并两个集合?

把其中一个集合的根作为另一个根的子节点,p[x] = y。

可以看到,第二步的时间复杂度比较高。如何优化呢? 用到路径压缩方法,直接将所有点,直接指向根节点。这是一个很好的加速,可以看成O(1)的时间复杂度了。并查集还有另外一个优化,按秩合并,但优化效果没有那么明显。写代码的时候不会写按秩合并的优化的。只会写这个路径压缩。

并查集的核心操作,只有两行:

int find(int x) // 返回x的祖宗节点,应用了路径压缩
{
    if (p[x] != x) p[x] = find(p[x]); // 如果p[x]不等于x,则令p[x]等于x的祖宗节点
    return p[x]; // 返回p[x],也就是返回了x的祖宗节点
}

这时给定了两个元素a和b,如果合并a、b所在集合的操作是:

p[find(a)] = find(b);  // 将a的根节点变为b的根节点的子节点,“认爹”

如果判断a、b是否在同一个集合:

if (find(a) == find(b))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值