[算法学习记录] 并查集(附例题)

news/2025/3/13 18:19:49/文章来源:https://www.cnblogs.com/Pingjiadoge/p/18766324

并查集简介

并查集是一种重要的数据结构,主要用于实现节点之间的合并查询操作(例如判断两个节点是否属于同一个连通块(共享同一个父节点的节点组成的集合叫连通块)),在解决不相交集合时有很大的用处;并查集同样常用于处理无向图,来描述接点的连通性,在初始化时,每个节点默认指向自己;
在并查集中,每一个节点有且仅有一个父节点(我喜欢称它为根)。

并查集的实现

  1. 寻找父节点
    我们可以通过递归的方式一路向上寻找,直到找到父节点,示例代码:
int root(int x)
{return pre[x] == x ? x : root(pre[x]);
}

这种方法有一种缺陷,如果节点连接成一条长链,那么每次查找父节点时,时间复杂度均为O(n),很显然,如果数据量稍微大一点,时间复杂度也会变得非常大,所以我们可以对并查集进行路径压缩,使每个节点直接指向父节点,而不是每次都重新查询,这样均摊下来每次查询的复杂度就会接近O(1),图例及代码如下:

路径压缩前

路径压缩后
  1. 节点的连接
    在初始状态下,各个节点的父节点都是它本身,我们需要根据题目给出的数据对节点所在的集合进行连接,只要我们把它们的父节点相连,就可以实现两集合的连接,代码实现如下:
void merge(int x, int y)
{pre[root(x)] = root(y);//当然pre[root(y)] = root(x);也是可以的
}

例题

星码 P68 联通块问题
我们已经知道联通块就是公用一个父节点的点的集合,而并查集就是用来解决这种问题的,我们可以把每个节点都存到并查集里面,在对它们进行遍历只要出现一个父节点sz[root(i)]就+1(sz数组是记录个集合大小的数组),之后可以创建一个数组v,在对sz进行遍历,为避免重复,只有root(i)=i时才把联通块大小放入数组v中,完成以上操作后,输出数组v即可。代码如下:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;const int N = 2e5 + 5;
int pre[N], sz[N];
//pre数组存储的的是每个节点的指向,sz数组存储的是每个集合的大小(根相同的点组成连通块)int root(int x)
{return pre[x] = (pre[x] == x ? x : root(pre[x]));
}
//寻找根节点的函数(采用了路径压缩,每个点直接指向他的根)
/*
不采用路径压缩(本题数据量较大,会超时)
int root(int x)
{return pre[x] == x ? x : root(pre[x]);
}
*/void solve()
{int n, m; cin >> n >> m;for (int i = 1; i <= n; i++) pre[i] = i;//并查集默认初始化为指向自己while (m--){int x, y; cin >> x >> y;pre[root(x)] = root(y);}//x,y间存在无向边,说明两点连结将两者的根相连即代表两节点相连vector<int> v;//存储连通块的大小for (int i = 1; i <= n; i++) sz[root(i)]++;//统计连通块大小(跟相同即为同一个连通块)for (int i = 1; i <= n; i++) if (root(i) == i) v.push_back(sz[i]);//找到根节点,记录连通块大小sort(v.begin(), v.end());//从小到大排序for (const auto &val : v) cout << val << " ";//输出
}int main()
{ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);int _ = 1;while (_--) solve();return 0;
}

本人初学算法,文章难免有纰漏,如有错误,敬请指正。

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

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

相关文章

Electron 进程间通信(IPC)方法详解

Electron 是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架,它是基于 Chromium 和 Node.js 构建的,而 Chromium 本身是采用多进程架构的,所以 Electron 也是多进程的。 Electron 是一个多进程框架,它的进程主要分为两类:主进程(Main Process) 和 渲染进程(R…

从零开始的web前端学习-JavaScript

JavaScript 是一种运行在客户端(浏览器)的编程语言,实现人机互动效果:网页特效(监听用户的某些行为并令网页进行反馈) 表单验证(针对表单数据的合法性进行判断) 数据交互(获取后台数据并渲染到前端)JavaScript 组成ECMAScript:基础语法核心 Web APIs:DOM(页面文档…

【Azure Service Bus】分享使用 Python Service Bus SDK 输出SDK内操作日志

问题描述 使用Python代码消费Service Bus中的消息,默认情况 Console 中的信息都是通过 print 打印输出。 有时候需要调查更多SDK中的日志,那么如何才能让SDK输出更多的日志呢?问题解答 方法就是引入 Logging SDK,然后再初始化 ServiceBusClient 对象时,设置logging_enabl…

nvm和nodejs安装

nvm和nodejs安装安装 nvm 全名 node.js version management,顾名思义是一个nodejs的版本管理工具。通过它可以安装和切换不同版本的nodejs。首先下载安装包,可以用GitHub上的,可以有点看,也可以用一些镜像,然后点击安装一直下一步即可。 然后打开命令行,可以用nvm -v指令…

dify文件上传到http节点

dify系统上传sys.files变量是Array[File]类型,由于 HTTP 请求节点不支持 Array[File] 上传,需要单独处理每个文件,以下是实现此功能的步骤: 添加迭代节点 迭代输入选sys.files 输出选http请求body http请求body类型选form-data 键值选迭代的item.File 本文使用dify版本为0.…

可行性分析(第五组)

目录 第1章 系统分析 1.1 可行性分析 1.1.1 技术可行性分析 1.1.2 经济可行性分析 1.1.3 社会可行性分析 1.1.4 法律可行性分析 1.2 系统流程分析 1.2.1 系统开发总流程 1.2.2 登录流程 1.2.3 系统操作流程 1.2.4 系统性能分析 第1章 可行性分析 1.1可行性分析 下面分别从技术可…

C# 子窗体中调用父窗体中的方法(或多窗体之间方法调用)

看似一个简单的功能需求,其实很多初学者处理不好的,很多朋友会这么写:C# Code://父窗体是是frmParent,子窗体是frmChildA //在父窗体中打开子窗体 frmChildA child = new frmChildA(); child.MdiParent = this; child.Show();//子窗体调父窗体方法: //错误的调用!!!!!!!! …

Qt HTTP模块——调用API对话DeepSeek

HTTP模块 Qt的网络模块(QtNetwork)支持HTTP/HTTPS协议,提供异步、非阻塞的API,实现客户端与服务器之间的 HTTP 请求与响应交互。核心类:QNetworkAccessManager:负责协调网络操作(如GET/POST请求),管理请求队列和返回的响应。 QNetworkRequest:封装HTTP请求的详细信息…

Linux下环境变量

Linux打印环境变量: echo $PATH

GitLearning

创建新仓库 创建新文件夹,打开,然后执行 git init创建新的 git 仓库 (也可以直接 git clone 远程仓库) git clone /path/to/repository git clone username@host:/path/to/repository工作流 本地仓库由 git 维护的三棵树。第一个是工作目录,它持有实际文件;第二个是暂存区(…

ai理解需求生成测试用例-deepseek

ai:把用例捡起来!把用例捡起来! 给大模型需求文档,让它完成设计用例,编写用例,包括功能用例、接口用例、自动化测试用例,自执行~最后发送至工作群中 直接使用deepseek即可 执行一下看看: 调用ds分析需求: 生成功能/接口用例: 生成自动化用例: 看一下自动生成的功…