算法笔记p328_并查集

并查集的定义

并查集是一种维护集合的数据结构,支持下面两个操作:

  1. 合并:合并两个集合。
  2. 查找:判断两个元素是否在一个集合。

并查集用一个数组实现:

int father[N];
  1. father[i]表示元素i的父亲结点,而父亲结点本身也是这个集合内的元素(1 ≤ i ≤ N)。
  2. 如果father[i] == i,则说明元素i是该集合的根结点。
  3. 对同一个集合来说只存在一个根结点,且将其作为所属集合的标识

并查集的基本操作

初始化

一开始,每个元素都是独立的一个集合,因此需要令所有father[i]等于i():

for (int i = 1; i <= N; ++i)
    father[i] = i;

查找

对给定结点寻找其根结点(即father[i] == i的结点)的过程。

  1. 递推代码
// findFather函数返回元素x所在集合的根结点
int findFather(int x) {
    while (x != father[x])  // 如果不是根结点,继续循环
        x = father[x];      // 获得自己的父亲结点
    return x;
}
  1. 递归代码
int findFather(int x) {
    if (x == father[x])
        return x;
    else
        return findFather(father[x]);
}

合并

  1. 对于给定的两个元素a、b,判断它们是否属于同一集合。可以调用上面的查找函数,对这两个元素a、b分别查找根结点,然后再判断其根结点是否相同。
  2. 合并两个集合:在①中已经获得了两个元素的根结点faA和faB,因此只需要把其中一个的父亲结点指向另一个结点即可。如令father[faA] = faB。
  3. 在合并过程中,只对两个不同集合进行合并,如果两个元素在相同的集合中,则不进行合并操作。这样保证了在同一个集合中一定不会产生环,即并查集产生的每个集合都是一棵树
void Union(int a, int b) {
    int faA = findFather(a);    // 查找a的根结点,记为faA
    int faB = findFather(b);    // 查找b的根结点,记为faB
    if (faA != faB)             // 如果不属于同一个集合
        father[faA] = faB;      // 合并它们
}

路径压缩

把当前查询结点的路径上的所有结点的父亲都指向根结点,查找的时候就不需要一直回溯去找父亲了,查询的复杂度可以降为O(1)。

  1. 按原先的写法获得x的根结点r。
  2. 重新从x开始走一遍寻找根结点的过程,把路径上经过的所有结点的父亲全部改为根结点r。

递推版本:

int findFather(int x) {
    int a = x;              // 保存原先的x
    while (x != father[x])  // 寻找x的根结点
        x = father[x];      // 循环结束后,x存放的是根节点
    // 下面把路径上的所有结点的father都改为根结点
    while (a != father[a]) {
        int z = a;          // 保存原先的a
        a = father[a];      // a回溯父亲结点
        father[z] = x;      // 将原先的结点a的父亲改为根结点x
    }
    return x;               // 返回根结点
}

递归版本:

int findFather(int x) {
    if (x == father[x])                 // 找到根结点
        return x;
    else {
        int r = findFather(father[x]);  // 递归寻找father[x]的根结点r
        father[x] = r;                  // 将根结点r赋值给father[x]
        return r;                       // 返回根结点r
    }
}

或者

int findFather(int x) {
    if (x != father[x])                     // x不是根结点
        father[x] = findFather(father[x]);  // 找到x父亲结点根结点,并做路径压缩
    return father[x];                       // 返回根结点
}
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值