图论:并查集

入门

久闻并查集的大名,今天来一探究竟,到底什么是并查集,并查集有什么用?

并查集(Disjoint Set Union, DSU)是一种处理不相交集合的合并及查询问题的数据结构。

其实并查集的作用主要就有两个:

1、将两个元素添加到同一个集合

2、判断两个元素是否在同一个集合内

碰到诸如此类的问题,就可以条件反射的去想到用并查集来解决了。

首先就是预处理的操作了只需要将所有的点连向自己即可:

void pre_handle()
{for(int i=0;i<n;i++) father[i] = i;
} 

然后就是添加函数和判断函数了,这两个函数都基于查找函数,要首先查找到两个点的根节点是谁

普通的查找函数:查找函数是基于递归来实现的,就是不断的递归去找父节点的父节点知道根节点

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

但是这样一直递归无疑是一个非常浪费时间的过程,每次查找一个点的根节点的时候都要从这个点开始递归到根节点,其实这个过程中做了很多重复的东西,在一个点递归完之后,他的所有父节点是不就就无需再递归了呢,所以就要考虑能不能把路径压缩,其实只需要在递归的过程中,将每个点的父节点都存为根节点,这样在下一次查找的时候,既可以直接根据这个点存的值直接找到该点的根节点了,比如一开始的1->2->3->4 当find(1)的时候在递归的过程中就变为了1->4, 2->4, 3->4 这样在下一次找2的根节点的时候就直接递归一次即可返回4,这个操作在较深的图中是很有用的!

int find(int x)
{if(father[x] == x) return x;father[x] = find(father[x]);//直接将根节点赋值给x的父节点 相当于深度变为了两层return father[x];
}

这样是不是很妙呢?有了find函数,接下来的添加(join)和判断(is_same)函数就很容易实现了。

join:

void join(int x,int y)
{x = find(x);//x直接存x的根节点y = find(y);//y同样存y的根节点if(x==y) return ;//如果x和y的根节点相同就说明本来就在同一个集合内father[x] = y;//让x的根节点指向y的根节点 就说明此时两个集合已经联通了
}

is_same:

bool is_same(int x,int y)
{x = find(x);y = find(y);return x==y ? true : false;
}

理论完成,下面开始做几道题来练练手吧!

寻找存在的路径 

题目链接:寻找存在的路径

这是并查集的基础模板的应用,按要求将联通的两个点加入到同一个集合中即可。

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define pii pair<int,int>
#define fi first
#define se second
const int N = 110;
vector<int> father(N);
int n,m;
void pre_handle()
{for(int i=1;i<=n;i++) father[i] = i;
} 
int find(int x)
{if(x == father[x]) return x;father[x] = find(father[x]);return father[x];
}
void join(int x,int y)
{x = find(x);y = find(y);if(x == y) return ;father[x] = y;//两个集合联通
}
bool is(int x,int y)
{x = find(x);y = find(y);return x==y ? true : false;
}
void solve()
{cin>>n>>m;pre_handle();for(int i=1;i<=m;i++){int u,v;cin>>u>>v;join(u,v);}int u,v;cin>>u>>v;if(is(u,v)) cout<<"1"<<endl;else cout<<"0"<<endl;
}signed main()
{IOSint T=1;
//	cin>>T;while(T--) solve(); return 0;
} 

冗余的边 

题目链接:冗余的边

这道题虽然是看着像一个复杂的树或图问题,其实本质上就是判断当前的边所连接的两个点之前可达不可达,如果可达(联通)的话那么此时的边就是冗余的了,又因为联通无环无向图就是一颗树所以只有一条冗余的边。那么只需要用并查集去判断是否可达(在一个集合内)即可。

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define pii pair<int,int>
#define fi first
#define se second
const int N = 1010;
vector<int> father(N);
int n;
int ans1,ans2;
void pre_handle()
{for(int i=1;i<=n;i++) father[i] = i;
} 
int find(int x)
{if(x == father[x]) return x;father[x] = find(father[x]);return father[x];
}
void join(int x,int y)
{x = find(x);y = find(y);if(x == y) return ;father[x] = y;//两个集合联通
}
bool is(int x,int y)
{x = find(x);y = find(y);return x==y ? true : false;
}
void solve()
{cin>>n;pre_handle();for(int i=1;i<=n;i++){int u,v;cin>>u>>v;if(is(u,v))//只要u和v在同一个集合中(可达 联通)就说明此时的边冗余了{ans1 = u;ans2 = v;}else join(u,v);}cout<<ans1<<" "<<ans2<<endl;
}signed main()
{IOSint T=1;
//	cin>>T;while(T--) solve(); return 0;
} 

冗余的边II 

题目链接:冗余的边II

这道题原本是一个有向树,因为多了一条冗余的边导致成为了一个图,所以我们就要判断哪些情况是导致冗余的原因,大体上分为两种

1.有向树的性质就是除了根节点的入度为0,其他的节点入度为1,入度为2就说明不是树了

2.入度也可能都是1,但是如果构成环了也就不是树了

但是第一种情况又可以细分,入度为2可能只有其中一条边是冗余的(入度为0的根节点与其相连的时候 这条边就不是一个冗余的边)所以要在入度为2的时候判断一下是否能删

可以利用并查集来判断是否有环!

  1. 题目特殊条件

    • 图是由"有向树添加一条边"构成的

    • 这意味着原始结构是一棵有向树(n-1条边)

    • 添加一条边后(共n条边),只可能产生两种违规情况:
      a) 某个节点入度变为2
      b) 形成一个环

  2. 并查集的适用性

    • 虽然并查集通常用于无向图,但这里我们关注的是"连接性"而非方向

    • 判断是否有环时,方向不重要(有向环必然包含无向环)

    • 判断连通性时,方向也不重要(我们只需知道节点是否相连)

  3. 方向信息的处理

    • 入度统计单独处理(用indegree数组)

    • 并查集只负责检测环和连通性

    • 两个部分各司其职,共同解决问题

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define pii pair<int,int>
#define fi first
#define se second
const int N = 1010;
vector<int> father(N),indegree(N,0),a;//定义并查集数组 入度数组 以及入度为2的数组
vector<pii> e(1);//存所有的两两有连接的边 不需要建树 只需要用入度和并查集统计边即可
int n;
void init()//初始化并查集
{for(int i=1;i<=n;i++) father[i] = i;
}
int find(int x)
{if(x == father[x]) return x;return father[x] = find(father[x]);
}
void join(int x,int y)
{x = find(x);y = find(y);if(x == y) return ;father[x] = y;
}
bool is(int x,int y)
{x = find(x);y = find(y);return x==y ? true : false;
}
bool istree(int x)//删除x这条边之后是否是一棵树
{//判断连通性时,方向并不重要(我们只需知道节点是否相连)init();for(int i=1;i<=n;i++){if(i == x) continue;if(is(e[i].fi,e[i].se)) return false;//成环了 不是有向树了join(e[i].fi,e[i].se);}return true;
}
void solve()
{cin>>n;init();for(int i=1;i<=n;i++){int u,v;cin>>u>>v;e.push_back({u,v});indegree[v]++;}for(int i=1;i<=n;i++){if(indegree[e[i].se] == 2) a.push_back(i);//将导致入度为2的边的编号存入}if(a.size()>0)//size==2{if(istree(a[1]))//因为要输出标准输入中靠后的一个 所以先判断a[1]cout<<e[a[1]].fi<<' '<<e[a[1]].se<<endl;else//如果删除a[1]这条边的话还是一个图 那么就一定是另一条边了(因为只会多出来一条边)cout<<e[a[0]].fi<<' '<<e[a[0]].se<<endl;}else//第三种情况{init();//找冗余的边 用并查集 判断成环的那条边//判断是否有环时,方向并不重要(有向环必然包含无向环)for(int i=1;i<=n;i++){if(is(e[i].fi,e[i].se)){cout<<e[i].fi<<' '<<e[i].se<<endl;return ;}elsejoin(e[i].fi,e[i].se);}}
}signed main()
{IOSint T=1;
//	cin>>T;while(T--) solve(); return 0;
} 

总之,并查集应用于无向图中,目的是看两个点是否联通或者两个集合是否联通以及将两个点加入到同一个集合当中构成联通性。 

这样就将并查集的两个主要作用给介绍完了,期待后续的迪杰斯特拉算法吧!

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

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

相关文章

告别静态文档!Oracle交互式技术架构图让数据库学习“活“起来

&#x1f5fa;️ 当数据库架构图学会"互动" 想象一下&#xff0c;你正在学习Oracle数据库架构&#xff0c;面对密密麻麻的静态文档和复杂的组件关系图&#xff0c;是不是常常感到&#xff1a; 像在迷宫里找路&#xff0c;不知道组件间如何协作&#xff1f;想深入了…

day62-可观测性建设-全链路监控zabbix+grafana

&#x1f31f;监控api接口 &#x1f50d;监控zabbix-api接口 生成API tokens命令行测试 curl -s -X POST -H "Content-Type: application/json-rpc" -d {"jsonrpc": "2.0","method": "host.get","params": {&quo…

通过Deepseek找工作

推送的结果如下,对应的AI提示词在底部: 计算机方向远程工作职位汇总 整合全球远程技术岗位 | 支持全地域远程办公 | 涵盖开发、安全、云计算等方向 覆盖方向:8+个技术领域 薪资范围:10K-40K/月 工作模式:100%远程 远程技术职位列表 职位名称 技能要求 经验要求 薪资…

vscode文件颜色,只显示自己更改的文件颜色、刚git下来的库,vscode打开后,显示所有文件都被修改了

问题&#xff1a;git新的库&#xff0c;然后我用vscode打开&#xff0c;默认显示所有的文件都更改了&#xff0c;但是我打开他们修改的对比&#xff0c;没有显示任何有被修改的地方&#xff0c;是怎么回事 linux/wsl下这么设置就可以了&#xff1a;git config core.autocrlf in…

基于ENMeval包的MaxEnt模型参数优化总结

MaxEnt模型参数优化1. MaxEnt模型优化&#xff1a;增加RM&#xff0c;降低模型过拟合风险&#xff0c;简易模型&#xff0c;平滑响应曲线&#xff0c;增强模型可解释性和转移性&#xff08;生物入侵&#xff09;2. 默认参数&#xff1a;FCLQHP&#xff0c;RM12.1. 基于优化的 M…

Docker实践:使用Docker部署blog轻量级博客系统

Docker实践&#xff1a;使用Docker部署blog轻量级博客系统一、blog系统介绍1.1 blog介绍1.2 个人博客系统介绍1.3 个人博客使用场景二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本四、…

专题:2025电商增长新势力洞察报告:区域裂变、平台垄断与银发平权|附260+报告PDF、原数据表汇总下载

原文链接&#xff1a;https://tecdat.cn/?p43416 当茂名果农对着镜头用方言喊出“荔枝现摘现发”&#xff0c;2小时卖出83万元&#xff1b;当65岁的上海阿姨通过“子女代付”买到人生第一台智能冰箱——2025年的电商战场&#xff0c;正在上演三重革命&#xff1a;新兴市场的增…

数字化转型-AI落地金字塔法则

前言 人工智能必须要跟传统产业结合&#xff0c;融入传统产业&#xff0c;才能落地&#xff0c;才能产生巨大的倍增个几何级效果&#xff01;&#xff01; AI不应该停留在工具层面&#xff0c;AI不仅仅是工具&#xff0c;不仅仅是硬件和软件&#xff0c;而是软硬结合。人工智能…

SQL Server 字段类型选型指南:什么数据用什么字段

目录 一、数值型数据 二、日期与时间数据 三、字符串与文本数据 四、布尔值与状态码 五、二进制与文件数据 六、唯一标识符&#xff08;GUID&#xff09; 七、枚举与代码表设计 八、存储优化小结 九、总结 在数据库设计中&#xff0c;字段类型&#xff08;数据类型&am…

酷暑来袭,科技如何让城市清凉又洁净?

烈日下的身影&#xff0c;不该被“炙烤”的担当又是一年盛夏&#xff0c;城市的血管在高温下脉动&#xff0c;柏油马路仿佛要融化&#xff0c;空气中弥漫着灼热的气息。此刻&#xff0c;你是否曾留意过那些身影&#xff1f;在烈日下&#xff0c;他们依旧坚守岗位&#xff0c;用…

传统框架与减震楼盖框架地震动力响应分析与有限元模拟

传统框架与减震楼盖框架地震动力响应分析与有限元模拟 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家,觉得好请收藏。点击跳转到网站。 摘要 本文针对传统钢框架和减震楼盖钢框架两种结构体系,建立了水平地震作用下的动力学模型,推…

Java集合去重

✅ 方式一&#xff1a;TreeSet Comparator最优雅的一种&#xff0c;适用于对象中某个字段唯一的去重&#xff08;如 partyAId&#xff09;List<PartyACompanyVO> result contractDOS.stream().map(contract -> {PartyACompanyVO vo new PartyACompanyVO();vo.setPa…

Qt字符串处理与正则表达式应用

一、Qt字符串处理基础 在Qt应用程序开发中&#xff0c;字符串处理是一项常见且重要的任务。Qt提供了强大而灵活的字符串处理功能&#xff0c;能够满足各种复杂的文本处理需求。 1.1 QString类概述 QString是Qt中处理字符串的核心类&#xff0c;它基于Unicode编码&#xff0c…

qt5静态版本对应的pcre编译

下载 https://sourceforge.net/projects/pcre/files/pcre/8.45/ 不同版本qt对应不同pcre 编译 启动vs2013的开发人员命令&#xff0c;可以找到cl程序 nmake环境设置到系统path中 cd C:\pcre-8.45 mkdir build_static cd build_static cmake .. -G "NMake Makefiles" …

JimuReport 积木报表 v2.1.1 版本发布,免费开源的报表和大屏

项目介绍 积木报表&#xff0c;是一款免费的数据可视化报表&#xff0c;含报表、打印、大屏和仪表盘&#xff0c;像搭建积木一样完全在线设计&#xff01;功能涵盖&#xff1a;复杂报表、打印设计、图表报表、门户设计、大屏设计等&#xff01; 分两大模块&#xff1a;JimuRepo…

基于python django的农业可视化系统,以奶牛牧场为例

摘 要 本文课题围绕畜牧业高质量发展中牧场管理的现状&#xff0c;现代牧场饲养模式上存在的数据比较零碎、饲养过程中容易经验主义、生产产量不稳、产出效益低、奶牛体况的不合理等现状&#xff0c;设计了多参数大数据智能牧场生产管理决策支撑体系。以牧场信息系统的建设为背…

无人机吊舱与遥控器匹配技术解析

一、 无人机吊舱如何与遥控器“对上暗号”&#xff1f;在无人机执行物资投送、电力巡检、灾害搜救等任务时&#xff0c;吊舱&#xff08;即悬挂于机身下方的任务设备&#xff09;常成为核心作业单元。但要让遥控器“指挥”吊舱&#xff0c;两者必须实现双向通信协议互通、电气接…

C#模拟pacs系统接收并解析影像设备数据(DICOM文件解析)

上篇文件介绍了什么dicomhttps://blog.csdn.net/qq_39569480/article/details/149641920?spm=1001.2014.3001.5502 本篇文章我们来使用fo_dicom接收并解析dicom文件。 文章结尾附源码。 1.开发环境 visual studio 2019 .netframwork 4.8 2.关键知识点 dicom三要素为 AE t…

在 IntelliJ IDEA 中打开这个用于设置 Git 用户名(Name)和邮箱(Email)的特定弹窗

要在 IntelliJ IDEA 中打开这个用于设置 Git 用户名&#xff08;Name&#xff09;和邮箱&#xff08;Email&#xff09;的特定弹窗&#xff08;如下图&#xff09;&#xff0c;可以通过以下几种常见方法触发&#xff1a;https://i.im.ge/2024/07/16/Kt6r1i.IDE-Git-UserName-Co…

redis 源码阅读

官网下载zip&#xff1a; 本文即是文件创建时间时候的版本~ 文章目录目录结构/srcint main()服务端 server足够的熵值 entropyumask掩码系统初始化*重启机制&#xff1a;保存执行数据 以便后续重启服务哨兵模式 sentinelrdb aof解析命令行参数声明实现的位置目录结构 目录/文…