并查集[讲课留档]

并查集(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;elsereturn 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;elsereturn 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.mzph.cn/pingmian/42148.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

通过SDK使用百度智能云的图像生成模型SDXL

登录进入百度智能云控制台&#xff0c;在模型广场按照图像生成类别进行筛选&#xff0c;可以找到Stable-Diffusion-XL模型。点击Stable-Diffusion-XL模型的API文档后在弹出的新页面下拉可以找到SDK调用的说明。 import qianfandef sdxl(file: str, prompt: str, steps: int 2…

MySQL——PreparedStatement对象

PreparedStatement可以防止SQL注入&#xff0c;效率更高。 1. 增 public class TestInsert {public static void main(String[] args) throws SQLException {Connection conn null;PreparedStatement ps null;ResultSet rs null;try {conn jdbcUtils.getConnection();// …

矩阵分析与应用1-矩阵代数基础

矩阵分析与应用1-矩阵代数基础 1 矩阵的基本运算2 矩阵的初等变换3 向量空间、线性映射与Hilbert空间4 内积与范数5 随机向量6 矩阵的性能指标7 逆矩阵与伪逆矩阵8 Moore-Penrose逆矩阵9 矩阵的直和与Hadamard积10 Kronecker积与Khatri-Rao积11 向量化与矩阵化12 稀疏表示与压缩…

金丝键合强度测试仪试验条件要求:键合拉脱/引线拉力/剪切力等

金丝键合强度测试仪是测量引线键合强度&#xff0c;评估键合强度分布或测定键合强度是否符合有关的订购文件的要求。键合强度试验机可应用于采用低温焊、热压焊、超声焊或有关技术键合的、具有内引线的器件封装内部的引线-芯片键合、引线-基板键合或内引线一封装引线键合&#…

华为机试HJ23删除字符串中出现次数最少的字符

华为机试HJ23删除字符串中出现次数最少的字符 题目&#xff1a; 实现删除字符串中出现次数最少的字符&#xff0c;若出现次数最少的字符有多个&#xff0c;则把出现次数最少的字符都删除。输出删除这些单词后的字符串&#xff0c;字符串中其它字符保持原来的顺序。 想法&…

第三方商城对接重构(HF202407)

文章目录 项目背景一、模块范围二、问题方案1. 商品模块整体来说这块对接的不是太顺利&#xff0c;梳理了几条大概的思路&#xff1a; 2. 订单模块3. 售后4. 发票5. 结算单 经验总结 项目背景 作为供应商入围第三方商城成功&#xff0c;然后运营了一段时间&#xff0c;第三方通…

PostgreSQL和Mysql的对比

PostgreSQL 是一种功能强大、开源的对象关系型数据库管理系统&#xff0c;广泛用于企业级应用和复杂数据查询。以下是一些关于 PostgreSQL 的基本介绍及其主要特性&#xff1a; 基本介绍 开源和社区支持&#xff1a;PostgreSQL 是一个开源项目&#xff0c;有一个活跃的社区支…

【vue2】记录mounted里面某三个接口调完之后才能执行其他方法的写法

背景 vue2&#xff0c;异步操作并行运行 需求 某个页面现时需要一些接口请求回来的枚举值作为查询条件可供选择&#xff0c;然后将这些查询条件作为入参&#xff0c;请求列表数据。这就造成了我需要先调用枚举值接口&#xff0c;等枚举值接口调用完了&#xff0c;再调查询接…

Docker部署Seata与Nacos整合

本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 Docker部署Seata与Nacos整合 Docker 部署 Seata 与 Nacos 整合 运行所使用的 demo项目地址 …

提升系统稳定性:熔断、降级和限流策略详解

文章目录 前言一、熔断&#xff08;Circuit Breaker&#xff09;二、降级&#xff08;Degradation&#xff09;三、限流&#xff08;Rate Limiting&#xff09;四、应用案例五、小结推荐阅读 前言 随着互联网业务的快速发展&#xff0c;系统稳定性和高可用性成为现代分布式系统…

RedHat9 | Zabbix-Server监控服务部署

系统版本以及软件版本 使用的系统版本&#xff1a; Red Hat Enterprise Linux release 9.2 软件版本&#xff1a; zabbix-release-7.0-3.el9.noarchzabbix-web-7.0.0-release1.el9.noarchzabbix-web-mysql-7.0.0-release1.el9.noarchzabbix-web-deps-7.0.0-release1.el9.noar…

路径规划之基于二次规划的路径平滑Matlab代码

参考&#xff1a; 自动驾驶决策规划算法第二章第二节(上) 参考线模块_哔哩哔哩_bilibili 自动驾驶决策规划算法第二章第二节(下) 参考线代码实践_哔哩哔哩_bilibili QP函数&#xff0c;二次规划的逻辑 function [smooth_path_x,smooth_path_y] QP(path_x, path_y, w_cost_s…

STM32嵌入式工业机器人控制系统教程

目录 引言环境准备工业机器人控制系统基础代码实现&#xff1a;实现工业机器人控制系统 4.1 数据采集模块 4.2 数据处理与分析 4.3 运动控制系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;工业自动化与优化问题解决方案与优化收尾与总结 1. 引言 工业机器人控制系统…

element表格高度撑开div滚动失效解决方案

描述&#xff1a;当显示区域小于表格列表显示内容时 列表完全撑开表格 无法触发el-table组件的滚动事件 解决&#xff1a;包裹表格的div&#xff0c;加上display: flex;flex-direction: column;即可 <template><div class"table-wrap table-scrollable"&g…

Bahdanau 注意力中上下文变量 ′的公式解释

公式 (10.4.1) 是 Bahdanau 注意力模型中的一个关键公式&#xff0c;用于计算在解码时间步 ( t’ ) 的上下文变量 (\mathbf{c}_{t’})&#xff1a; [ \mathbf{c}{t’} \sum{t1}^T \alpha(\mathbf{s}_{t’ - 1}, \mathbf{h}_t) \mathbf{h}_t ] 下面对公式进行详细解释&#x…

【pytorch18】Logistic Regression

回忆线性回归 for continuous:y xwbfor probability output:yσ(xwb) σ:sigmoid or logistic 线性回归是简单的线性模型&#xff0c;输入是x&#xff0c;网络参数是w和b&#xff0c;输出是连续的y的值 如何把它转化为分类问题?加了sigmoid函数&#xff0c;输出的值不再是…

开闭原则的简单总结

简介 开闭原则指的是软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着: 对扩展开放:当需要新增功能时,可以通过添加新的代码来实现,而不是修改现有代码。对修改关闭:已经存在的、经过测试的代码不应该被修改。 优缺点 优点: 提高代码的可维护性和复用性降低…

java-HashSet 源码分析 1

## 深入分析 Java 中的 HashSet 源码 HashSet 是 Java 集合框架中的一个重要类&#xff0c;它基于哈希表实现&#xff0c;用于存储不重复的元素。HashSet 允许 null 元素&#xff0c;并且不保证元素的顺序。本文将详细分析 HashSet 的源码&#xff0c;包括其数据结构、构造方法…

2024阿里国际春招笔试

第一题 0 解题思路&#xff1a; 数据范围很大&#xff0c;肯定得找规律。 当n1时&#xff0c;0&#xff0c;1&#xff0c;结果为0 当n2时&#xff0c;00&#xff0c;01&#xff0c;10&#xff0c;11&#xff0c;结果为1 当n3时&#xff0c;000&#xff0c;001&#xff0c;010&a…

AWS IoT Core 权限管理指南

AWS IoT Core 是一项托管云服务,允许连接设备安全地与云应用程序和其他设备进行交互。有效管理 IoT Core 的权限对于确保设备和数据的安全至关重要。本文将详细介绍 IoT Core 的常用权限分类,并提供相应的 JSON 策略示例。 1. 概述 IoT Core 的权限可以大致分为以下几类: …