并查集[讲课留档]

并查集(DSU)

一些可以实现合并 查询 集合

简洁优雅的树型数据结构,主要用于解决一些元素分组的问题。可以管理一系列不相交的集合,并支持两种操作:

  • 合并(join):把两个不相交的集合合并为一个集合。
  • 查询(find):查询两个元素是否在同一个集合中。
前置芝士!
  • 连通图:由若干个顶点构成的子图,其中任意两个顶点之间都存在路径。

  • :有 n n n个结点, n − 1 n-1 n1条边的无向无环的连通图。

  • 森林:每个连通分量(连通块)都是树的图。按照定义,一棵树也是森林。

理论存在!
  • 集合的表示方式

用集合中的一个元素表示。(举个栗子……

  • 合并

A A A节点带着他的集合共同加入 B B B节点所在集合,直接让 B B B的祖宗成为 A A A的祖宗的父亲即可。

  • 查询

只需要一直查找父亲节点,直到出现父亲为自己时,说明找到了根节点。

当需要判断两个元素是否同在一个集合时,只需要找到每个元素所在集合的代表元素即可,一个元素不可身兼数职

实践开始!(基础版):
  • 初始化:一开始每个节点的父亲都是自己。
int f[N];
void init(int n){
    for (int i = 1; i <= n; ++i){
        fa[i] = i;
    }
}
  • 查询:找到根节点。
int find(int x){
	if(f[x] == x)
        return x;
    else
        return find(f[x]);
}
  • 合并:将A的集合的代表元素的父亲设为B的集合的代表元素
void join(int a,int b){
	f[find(a)] = find(b);
}
例题

并查集模板:https://www.luogu.com.cn/problem/P3367

AC代码:

#include<bits/stdc++.h>
using namespace std;
int f[20005];

void init(int n) {
    for (int i = 1; i <= n; ++i) {
        f[i] = i;
    }
}

int find(int x) {
    if (f[x] == x) {
        return x;
    } else {
        return f[x] = find(f[x]);
    }
}

void join(int x, int y) {
    int a = find(x), b = find(y);
    if (a != b) {
        f[a] = b;
    } 
}

int main() {
    int n, m;cin >> n >> m;
    init(n);
    while (m--) {
        int op, x, y;cin >> op >> x >> y;
        if (op == 1) {
            join(x, y);
        } else {
            if (find(x) == find(y)) {
                cout << "Y\n";
            } else {
                cout << "N\n";
            }
        }
    }
    return 0;
}
进阶之路!
  • 路径压缩:

    极端情况下,若每次将新元素合并到某集合的末尾,最终会构成一条链,随着链长的增加,从尾到头的查询效率会越来越低,查询的复杂度是由树的高度决定的,那么最理想的情况是所有人的父亲都是该集合的代表元素,此时树只有两层,因而出现了路径压缩优化。

    路径压缩的trick在于查询,在查询过程中不难看出,当从某个节点出发去寻找它的根节点时,会途径一系列的节点,在这些节点中,除了根节点外,我们可以顺手将其余所有节点都把直接父亲更改为根节点。基于这样的思路,我们可以通过递归来逐层修改返回时的某个节点的直接父亲(即f[x]的值)。简单说来就是将x到根节点路径上的所有点的父亲都设为根节点。

    int find(int x){
    	if(f[x] == x)return x;
    	return f[x]=find(f[x]);
    }
    

    但该优化的局限性也同样在于查询,由于压缩过程在查找时生效,于是只有查找了某个元素到根节点后,才能对该查找路径上的各节点进行路径压缩,即第一次执行查找操作的时候是没有实现压缩效果的,只有在之后的才有效,且每次压缩只能压缩一条路径,所以压缩后的树仍可能是较为复杂的,于是出现了另外一种优化方式。

  • 按秩合并|启发式合并:

    按秩合并的主要思想是合并的时候把小的树合并到大的树以减少工作量。

    我们先来定义一下并查集的“秩”,有两种定义的方法:

    1. 树的高度
    2. 树的节点数

    我们在路径压缩之后一般采用第二种,因为第一种在路径压缩之后就已经失去意义了,按照第二种合并可以减少一定的路径压缩的工作量。(但其实也不会太多,所以一般来说路径压缩就够用了)

    单独采用路径压缩的时查询时间复杂度为 O ( l o g N ) O(log N) O(logN)

    如果我们把路径压缩和按秩合并合起来一起使用的话可以把查询复杂度下降到 O ( α ( n ) ) O(α(n)) O(α(n)),其中 α ( n ) α(n) α(n)为反阿克曼函数。阿克曼函数是一个增长极其迅速的函数,而相对的反阿克曼函数是一个增长极其缓慢的函数,所以我们在算时间复杂度的时候可以把他视作一个常数

    void join(int a,int b){
       int fa=find(a),fb=find(b);
       if(fa==fb) return;
       if(size[fa]>size[fb])
           swap(fa,fb);
       f[fa]=fb;
       size[fb]+=size[fa];
    }
    
成为大师!
  • 种类并查集

    团伙:https://www.luogu.com.cn/problem/P1892

    如何实现敌人的敌人是朋友?

    反集:并查集的反集适用于元素只有两种性质的题目,也就是说,这个元素如果不属于并查集维护集合,则其必定属于另一个集合,加一个n即可将元素的一个虚拟敌人存入反集,并且和另一个元素合并。

    如果我们要将两个性质不同的元素 a a a b b b合并,我么们可以用并查集合并a和b+n、b和a+n ,如果 c c c a a a的性质不同,我们继续用并查集合并a和c+n、c和a+n ,此时 b b b a a a性质相同,他们成功合并在一起。

    食物链(感兴趣可做):https://www.luogu.com.cn/problem/P2024

团伙AC代码:

#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
//#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
const int mod = 998244353;
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
const int inf = 0x3f3f3f3f;
const int N = 3e5 + 50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
int f[N];

int find(int a) {
    if (f[a] == a)
        return a;
    else
        return f[a] = find(f[a]);
}

void join(int a, int b) {
    if (find(a) != find(b))
        f[find(b)] = find(a);
}

void work() {
    int n,m;cin>>n>>m;
    for(int i=1;i<=2*n;++i){
        f[i]=i;
    }
    for(int i=1;i<=m;++i){
        char opt;int p,q;
        cin>>opt>>p>>q;
        //cout<<p<<" "<<q<<'\n';
        if(opt=='F'){
            join(p,q);
        }else{
            join(p,q+n);
            join(q,p+n);
        }
    }

    int ans=0;
    for(int i=1;i<=n;++i){
        if(f[i]==i)ans++;
    }
    cout<<ans<<'\n';
}

signed main() {
    io;
    int t=1;
    //cin >> t;
    while (t--) {
        work();
    }
    return 0;
}
  • 带权并查集

    不难发现所谓的并查集本质上也是一种树,由于路径压缩的存在,使得一个并查集树中,其被压缩过的子节点总是直接指向根节点,于是我们可以给根节点与子节点之间的边进行赋权,这个权值可以表示该节点与根节点之间的距离。

大师我悟了!
  • 写并查集时很容易忘记初始化导致 d e b u g debug debug时找不到错,务必务必务必先初始化

  • 注意!并查集无法以较低复杂度实现集合的删除

  • 大多数考察并查集的题目在我们想到用并查集的时候已经被解决了大半,即想到要用是极为重要的。

  • 在日后的学习过程中,并查集多数时刻会作为工具拥有更灵活的用法,例如最小生成树 K r u s k a l Kruskal Kruskal算法、记录区间实现快速跳区间。

祝大家学习愉快!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/764474.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ERP系统中有哪些模块?有哪些具体实现方案呢?

对于许多初次接触ERP系统的企业来说&#xff0c;可能会对系统中包含的模块和功能感到困惑。本文将详细介绍ERP系统中的主要模块&#xff0c;需要明确的是&#xff0c;ERP系统是一个庞大的系统&#xff0c;包含了多个模块&#xff0c;每个模块都有其独特的功能和作用。这些模块涵…

让新质生产力照进现实,智慧数据基础设施需要软硬兼施

数智化时代&#xff0c;什么才是企业与组织最大的差异化竞争力&#xff1f; 答案无疑是&#xff1a;数据。在生成式AI技术日新月异之际&#xff0c;发展新质生产力已成为产业共识&#xff0c;越来越多的企业意识到&#xff1a;数据乃一切运作的基础&#xff0c;是企业拥抱AI浪…

列出每个字符的位置

列出每个字符的位置 一行字母&#xff0c;部分连续。 ABCDEFGHIJ1puuuupppup 需要整理成字母位置的形式。 ABCDE1p1u2345p678u9p10 使用 SPL XLL spl("[(dE1(?)).groupop(~).(d(~1) / ~.concat())]",A1:J1)函数 E1 将多层序列转为单层&#xff0c;groupop分组后…

【前端环境1】安装nvm

【前端环境1】安装nvm 写在最前面一、卸载node二、下载nvm三、安装教程四、验证nvm安装五、nvm配置node常用命令 &#x1f308;你好呀&#xff01;我是 是Yu欸 &#x1f30c; 2024每日百字篆刻时光&#xff0c;感谢你的陪伴与支持 ~ &#x1f680; 欢迎一起踏上探险之旅&#…

接口测试流程及测试点!

一、什么时候开展接口测试 1.项目处于开发阶段&#xff0c;前后端联调接口是否请求的通&#xff1f;&#xff08;对应数据库增删改查&#xff09;--开发自测 2.有接口需求文档&#xff0c;开发已完成联调&#xff08;可以转测&#xff09;&#xff0c;功能测试展开之前 3.专…

“私域流量:解锁电商新机遇,共创数字化未来“

一、私域流量的战略意义再探 步入数字化浪潮的深处&#xff0c;流量已成为企业成长不可或缺的血液。与广泛但难以掌控的公域流量相比&#xff0c;私域流量以其独特的专属性和复用潜力&#xff0c;为企业铺设了通往深度用户关系的桥梁。它不仅赋能企业实现精准营销&#xff0c;…

MySQL-核心知识要点

1、索引的数据结构-Btree BTree的优势&#xff1a; B树的内节点无data&#xff0c;一个节点可以存储更多的K-V对。在构造树时&#xff0c;需要的内节点会更少&#xff0c;那么树的层级也会越低。查询一条数据时&#xff0c;1. 扫描的层级低&#xff0c;扫描过的节点更少&…

VBA字典与数组第十六讲:行、列数不相同的数组间运算规律

《VBA数组与字典方案》教程&#xff08;10144533&#xff09;是我推出的第三套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;字典是VBA的精华&#xff0c;我要求学员必学。7.1.3.9教程和手册掌握后&#xff0c;可以解决大多数工作中遇到的实际问题。…

分布式链路追踪Micrometer Tracing和ZipKin基础入门与实践

【1】概述 在分布式与微服务场景下&#xff0c;我们需要解决如下问题&#xff1a; 在大规模分布式与微服务集群下&#xff0c;如何实时观测系统的整体调用链路情况。 在大规模分布式与微服务集群下&#xff0c;如何快速发现并定位到问题。 在大规模分布式与微服务集群下&…

供应商管理软件:企业挑选新供应商的5个考量

在选择新的供应商时&#xff0c;企业必须进行细致的考量&#xff0c;这一决策对于依赖外部商品的零售商尤为关键。一段成功的合作伙伴关系不仅能够促进销售增长&#xff0c;还能提供稳定的服务支持。相反&#xff0c;失败的合作伙伴关系可能会导致客户不满、利润损失&#xff0…

一篇搞懂!LinuxCentos中部署KVM虚拟化平台(文字+图片)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f468;‍&#x1f4bb;Linux高级管理专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年6月28日15点11分 &#x1f004;️文章质量&#xff1a;94分 目录 ————前言———— KVM的优点 KVM…

机器人控制系列教程之Delta机器人运动学分析(2)

基于MATLAB的Delta机器人正向运动学模型求解 我们在上一篇推文 中&#xff0c;推导了Delta机器人的正向运动学&#xff0c;简单来说&#xff0c;就是我们可以通过机器人的末端位姿求解出对应的关节空间的角度&#xff08;位置&#xff09;。 最终我们分析该机器人的空间位置结…

服务器数据恢复—raid5阵列硬盘出现大量坏道的数据恢复案例

服务器存储数据恢复环境&故障&#xff1a; 一台DELL EqualLogic PS 4000存储中有一组由12块磁盘组建的raid5阵列&#xff0c;存储空间划分3个同等大小的卷&#xff0c;采用的VMFS文件系统。 两块硬盘指示灯亮黄色&#xff0c;raid5阵列崩溃&#xff0c;存储变得不可用。 服…

C++类型转换可调用对象

目录 C的四种可视性类型转换 1.static_cast 2.reinterpret_cast 3.const_cast 4.dynamic_cast C中的可调用对象 普通函数 函数指针 仿函数 Lambda表达式 包装器function bind C的四种可视性类型转换 C语言中的类型转换是不安全、不明确的&#xff0c;于是C就出了更…

跨境业务经验推荐:三大优秀的IP代理服务商

作为一名多年从事跨境业务的老手&#xff0c;今天我要给大家介绍几款绝对靠谱的IP代理服务商&#xff0c;保证让你的全球业务更加顺畅&#xff01; 1. 711Proxy 711Proxy以其优秀的性能和覆盖范围广而著称。对于跨境电商和国际业务来说&#xff0c;快速稳定的网络连接至关重要…

115V 400HZ远机位电源车在国际机场的推广与应用

随着我国航空业的快速发展&#xff0c;对于远机位电源车的需求也越来越迫切。远机位电源车可以为飞机提供稳定、可靠的电力&#xff0c;确保飞机在停机、起降、航行等环节中正常运行。在当前的航空技术中&#xff0c;115V 400HZ 远机位电源车技术发展及其在航空领域的应用逐年增…

把 AI 人机炼成高玩,游戏 AI 技术实践指南,码住!

今天&#xff0c;为大家深入浅出地讲明白上亚运的经典 IP《梦三国 2》&#xff0c;到底应用了哪些来自网易数智的 AI 黑科技。看完你就会觉得&#xff1a;原来做 AI&#xff0c;我也行&#xff01; 方案概述 游戏作为 AI 落地最佳的试验田&#xff0c;近年来已经产生了多个极具…

计算机系统基础(二)

1.数值数据的表示 为什么采用二进制&#xff1f; 二进制只有两种基本状态&#xff0c;两个物理器件就可以表示0和1二进制的编码、技术、运算规则都很简单0和1与逻辑命题的真假对应&#xff0c;方便通过逻辑门电路实现算术运算 数值数据表示的三要素 进位记数制&#xff08;十…

计算机缺少d3dcompiler_43.dll无法继续执行代码怎么修复

打开游戏或许软件程序时候&#xff0c;我们会经常遇到各式各样的问题&#xff0c;比如找不到d3dcompiler_43.dll无法继续执行代码就是非常常见的问题&#xff0c;今天我叫大家如何解决遇到d3dcompiler_43.dll丢失问题&#xff0c;也详细介绍d3dcompiler_43.dll文件是什么与丢失…

加油卡APP开发,汽车加油省钱新模式

随着社会生活水平的提高&#xff0c;汽车已经成为了家家户户的出行工具&#xff0c;汽车加油也就成为了居民日常出行必不可少的开销。为了让居民享受到更加便利、优惠的加油体验&#xff0c;加油卡APP由此产生&#xff0c;不仅方便了用户&#xff0c;也给汽车加油市场提供了更加…