当前位置: 首页 > article >正文

算法笔记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];                       // 返回根结点
}

http://www.kler.cn/a/273206.html

相关文章:

  • csp2024T3
  • Unreal5从入门到精通之如何解决在VR项目在头显中卡顿的问题
  • 桥接IC lt7911d linux 驱动
  • Nginx线程模型
  • Python毕业设计选题:基于django+vue的4S店客户管理系统
  • Docker-安装
  • LeetCode350:两个数组的交集Ⅱ
  • 腾讯云优惠券领取指南:让你省钱又省心
  • 文件系统I/O FATFS RW 源码分析
  • win修改图标自定义QQ桌面图标
  • 粤嵌6818开发板通过MobaXterm使用SSH连接开发板
  • 前端入职配置新电脑!!!
  • 力扣思路题:最长特殊序列1
  • kingbase 服务器配置(参数修改)
  • 关于学习的一点粗浅见解
  • Linux TCP参数——tcp_adv_win_scale
  • luceda ipkiss教程 63:器件端口延伸ExtendPorts
  • C++——字符串、读写文件、结构体、枚举
  • 【人工智能】英文学习材料03(每日一句)
  • 【字符串算法题】541. 反转字符串 II
  • es 聚合操作(二)
  • openstack迁移虚拟机--来自gpt
  • kerberos验证协议安装配置使用
  • 6语言交易所/多语言交易所php源码/微盘PHP源码
  • 数据结构 二叉树 力扣例题AC——代码以及思路记录
  • 由浅到深认识C语言(13):共用体