算法.图论-并查集
文章目录
- 1. 并查集介绍
- 2. 并查集的实现
- 2.1 实现逻辑
- 2.2 isSameSet方法
- 2.3 union方法(小挂大优化)
- 2.4 find方法(路径压缩优化)
- 3. 并查集模板
- 4. 并查集习题
- 4.1 情侣牵手
- 4.2 相似字符串组
1. 并查集介绍
定义:
并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题(即所谓的并、查)。比如说,我们可以用并查集来判断一个森林中有几棵树、某个节点是否属于某棵树等
并查集的常见的方法:
| 方法 | 作用 |
|---|---|
| int find (int) | 作用就是查找一个元素所在大集合的代表元素, 返回这个元素 |
| boolean isSameSet (int, int) | 判断传入的两个元素是不是同属一个大集合, 返回T/F |
| void union (int, int) | 合并传入的两个元素所代表的大集团(注意不仅仅是这两个元素) |
并查集的时间复杂的要求就是实现上述的操作的时间复杂度都是O(1)
下面是关于并查集的一些常见的操作的图示

2. 并查集的实现
2.1 实现逻辑
不论是哈希表的机构还是list的顺序结构或者是其他的常见的数据结构, 都不可以做到时间复杂度是O(1)的这个指标, 我们直接介绍实现的方式 --> 通过一个father数组以及size数组
关于这两个数组的含义:
| 数组 | 含义 |
|---|---|
| father | 下标i代表的是元素的编号, father[i]代表的是他的父亲节点 |
| size | 下标i代表的是元素的编号, size[i]代表的是这个节点的孩子节点的个数(包括本身) |

初态就是这个样子, 每一个元素的父亲节点都是其本身, 也就是说每一个节点本身就是其所在集合的代表节点, 然后这个集合的大小就是1
下面我们执行操作
step1 : union(a, b)
step2 : union(c, a)
下面是图示(图解一下操作1, 操作2其实是同理的)

上面的图解也说明了很多问题, 我们的树形结构的挂载的方式是, 小挂大(小的树挂到大树上)
此时进行了union操作之后的逻辑结构就是左下角所示, 此时我们 {a,b} 共属于一个集合, 进行find操作的时候, find(a) 的结果是 b, find(b) 的结果也是 b, 此时size数组中a的值不会再使用了, 因为这时a不可能是领袖节点了, 也就是说这个数据是脏数据…
2.2 isSameSet方法
其实正常来说我们的isSameSet方法和union方法都需要调用find方法, 但是find方法中的路径压缩的技巧是比较重要的, 所以我们单独拎出来放后面说(这里假设已经实现好了), 实现也是比较简单的, 只需要找到这两个元素的代表领袖节点看是不是一个就可以了
//isSameSet方法private static boolean isSameSet(int a, int b){return find(a) == find(b);}
2.3 union方法(小挂大优化)
解释一下小挂大概念, 在算法导论这本书中说到的是一种秩的概念, 本质上也是为了降低树(集团)的高度所做出的努力, 但这个不是特别必要的…, 也就是在两大集团合并的时候, 小集团(小数目的节点)要依附大集团而存在, 也就是合并的时候, 小集团要挂在大集团上面, 这样可以从一定程度上降低树的高度
代码实现如下
//union方法private static void union(int a, int b){int fa = find(a);int fb = find(b);if(fa != fb){sets--;if(size[fa] >= size[fb]){father[fb] = fa;size[fa] += size[fb];}else{father[fa] = fb;size[fb] += size[fa];}}}
2.4 find方法(路径压缩优化)
上面的union的小挂大优化, 其实不是特别必要的, 但是我们find方法中的路径压缩是一定要完成的, 如果没有路径压缩的话, 我们的时间复杂度的指标就不会是O(1)
路径压缩指的就是, 在find方法找到父亲节点的时候, 同时把我们的沿途所有节点的父亲节点都改为找到的父亲节点, 以便于操作的时候不用遍历一个长链去寻找父亲节点, 图解如下

假设我们执行find(a)操作, 就会如图所示把我们的沿途的所有节点的父亲节点都改为领袖节点e
我们借助的是stack栈结构, 或者是递归(其实就是系统栈)实现的
private static final int MAX_CP = 31;private static final int[] father = new int[MAX_CP];private static final int[] size = new int[MAX_CP];private static final int[] stack = new int[MAX_CP];//find方法(路径压缩的迭代实现)private static int find1(int a){int sz = 0;while(father[a] != a){stack[sz++] = a;a = father[a];}while(sz > 0){father[stack[--sz]] = a;}return father[a];}//find方法(路径压缩的递归实现)private static int find(int a){if(father[a] != a){father[a] = find(father[a]);}return father[a];}
3. 并查集模板
上面就是我们关于并查集最基本的分析, 我们提供几个测试链接测试一下
牛客并查集模板
//并查集的基本实现方式
import java.util.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.io.OutputStreamWriter;
import java.io.IOException;public class Main {private static final int MAXN = 1000001;private static final int[] father = new int[MAXN];private static final int[] size = new int[MAXN];private static final int[] stack = new int[MAXN];private static int cnt = 0;private static void build(int sz) {cnt = sz;for (int i = 0; i <= cnt; i++) {father[i] = i;size[i] = 1;}}private static int find(int n) {//下面就是扁平化(路径压缩的处理技巧)int capacity = 0;while (father[n] != n) {stack[capacity++] = n;n = father[n];}//开始改变沿途节点的指向while (capacity > 0) {father[stack[--capacity]] = n;}return father[n];}private static boolean isSameSet(int a, int b) {return find(a) == find(b);}private static void union(int a, int b) {//下面的设计就是小挂大的思想int fa = find(a);int fb = find(b);if (fa != fb) {if (size[fa] >= size[fb]) {father[fb] = fa;size[fa] += size[fb];} else {father[fa] = fb;size[fb] += size[fa];}}}//我们使用的是高效率的io工具(使用的其实就是一种缓存的技术)public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));StreamTokenizer in = new StreamTokenizer(br);PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));while (in.nextToken() != StreamTokenizer.TT_EOF) {int n = (int)in.nval;build(n);in.nextToken();int m = (int)in.nval;for (int i = 0; i < m; i++) {in.nextToken();int op = (int)in.nval;in.nextToken();int n1 = (int)in.nval;in.nextToken();int n2 = (int)in.nval;if (op == 1) {out.println(isSameSet(n1, n2) ? "Yes" : "No");} else {union(n1, n2);}}}out.flush();out.close();br.close();}
}
洛谷并查集模板
//并查集的基本实现方式
import java.util.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.io.OutputStreamWriter;
import java.io.IOException;public class Main {private static final int MAXN = 100001;private static final int[] father = new int[MAXN];private static final int[] size = new int[MAXN];private static final int[] stack = new int[MAXN];private static int cnt = 0;private static void build(int sz){cnt = sz;for(int i = 0; i <= cnt; i++){father[i] = i;size[i] = 1;}}private static int find(int n){//下面就是扁平化(路径压缩的处理技巧)int capacity = 0;while(father[n] != n){stack[capacity++] = n;n = father[n];}//开始改变沿途节点的指向while(capacity > 0){father[stack[--capacity]] = n;}return father[n];}private static boolean isSameSet(int a, int b){return find(a) == find(b);}private static void union(int a, int b){//下面的设计就是小挂大的思想int fa = find(a);int fb = find(b);if(fa != fb){if(size[fa] >= size[fb]){father[fb] = fa;size[fa] += size[fb];}else{father[fa] = fb;size[fb] += size[fa];}}}//我们使用的是高效率的io工具(使用的其实就是一种缓存的技术)public static void main(String[] args) throws IOException{BufferedReader br = new BufferedReader(new InputStreamReader(System.in));StreamTokenizer in = new StreamTokenizer(br);PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));while(in.nextToken() != StreamTokenizer.TT_EOF){int n = (int)in.nval;build(n);in.nextToken();int m = (int)in.nval;for(int i = 0; i < m; i++){in.nextToken();int op = (int)in.nval;in.nextToken();int n1 = (int)in.nval;in.nextToken();int n2 = (int)in.nval;if(op == 2){out.println(isSameSet(n1, n2) ? "Y" : "N");}else{union(n1, n2);}}}out.flush();out.close();br.close();}
}
4. 并查集习题
4.1 情侣牵手
leetcode765.情侣牵手题目链接

//本题的前置知识可能是置换环(这一题的并查集的思路尤其不好想)
class Solution {
//核心点的分析就是如果一个集合里面有k对情侣, 那么我们至少需要交换 k - 1 次private static final int MAX_CP = 31;private static final int[] father = new int[MAX_CP];private static final int[] size = new int[MAX_CP];private static final int[] stack = new int[MAX_CP];private static int sets = 0;//初始化并查集private static void build(int n){sets = n;for (int i = 0; i < n; i++) {father[i] = i;size[i] = 1;}}//find方法(路径压缩的实现)//find方法(路径压缩的递归实现)private static int find(int a){if(father[a] != a){father[a] = find(father[a]);}return father[a];}//isSameSet方法private static boolean isSameSet(int a, int b){return find(a) == find(b);}//union方法private static void union(int a, int b){int fa = find(a);int fb = find(b);if(fa != fb){sets--;if(size[fa] >= size[fb]){father[fb] = fa;size[fa] += size[fb];}else{father[fa] = fb;size[fb] += size[fa];}}}public int minSwapsCouples(int[] row) {int cpN = row.length / 2;build(cpN);for(int i = 0; i < row.length; i += 2){union(row[i] / 2, row[i + 1] / 2);}return cpN - sets;}
}
4.2 相似字符串组
leetcode839.相似字符串组

//简单的并查集的应用
class Solution {private static final int MAXN = 301;private static final int[] father = new int[MAXN];private static final int[] size = new int[MAXN];private static final int[] stack = new int[MAXN];private static int sets = 0;//初始化并查集的方式private static void build(int n){sets = n;for(int i = 0; i < n; i++){father[i] = i;size[i] = 1;}}//find方法private static int find(int a){int sz = 0;while(father[a] != a){stack[sz++] = a;a = father[a];}while(sz > 0){father[stack[--sz]] = a;}return father[a];}//isSameSet方法 private static boolean isSameSet(int a, int b){return find(a) == find(b);}//union方法private static void union(int a, int b){int fa = find(a);int fb = find(b);if(fa != fb){sets--;if(size[fa] >= size[fb]){size[fa] += size[fb];father[fb] = fa;}else{size[fb] += size[fa];father[fa] = fb;}}}public int numSimilarGroups(String[] strs) {int n = strs.length;int m = strs[0].length();build(n);for(int i = 0; i < n; i++){for(int j = i + 1; j < n; j++){if (find(i) != find(j)) {int diff = 0;for (int k = 0; k < m && diff < 3; k++) {if (strs[i].charAt(k) != strs[j].charAt(k)) {diff++;}}if (diff == 0 || diff == 2) {union(i, j);}}}}return sets;}
}
相关文章:
算法.图论-并查集
文章目录 1. 并查集介绍2. 并查集的实现2.1 实现逻辑2.2 isSameSet方法2.3 union方法(小挂大优化)2.4 find方法(路径压缩优化) 3. 并查集模板4. 并查集习题4.1 情侣牵手4.2 相似字符串组 1. 并查集介绍 定义: 并查集是一种树型的数据结构,用于处理一些不…...
elasticSearch常见命令及历史数据迁移
es这种非关系型数据库,感觉可视化效果不是很好,个人在操作中,习惯性通过简单的方式去访问。也是接触不久。只能出一些基操。共同学习记录,大家有好的操作也可留言备注。 1,常见命令 1)查询有哪些index&…...
WebLogic 漏洞复现
1、后台弱⼝令GetShell 默认账号密码:weblogic/Oracle123 weblogic常⽤弱⼝令:https://cirt.net/passwords?criteriaweblogic 这⾥注意, 单个账号错误密码5次之后就会⾃动锁定。 http://47.121.212.195:7001/console 2、登录后台后&#…...
web基础:域名、网页、HTML、web版本
文章目录 引言域名网站访问方式域名结构域名解析DNS解析过程 网页网页文件类型静态网页与动态网页常用动态网页编程语言 HTMLHTML 语法规则HTML 文件结构HTML 文件基本结构示例:常用 HTML 标签HTML文件基本结构 WEB版本 引言 web(World Wide Web&#x…...
【项目案例】物联网比较好的10+练手项目推荐,附项目文档/源码/视频
练手项目推荐 1 智能小车 项目功能介绍: 本项目由三部分组成:应用端(微信小程序)、设备端(Hi3861)、驱动端(UPS)。 1. 应用端,采用微信小程序作为应用端控制界面。在开…...
AWS注册时常见错误处理
引言 创建AWS账号是使用AWS云服务的第一步,但在注册过程中可能会遇到一些常见的问题。本文中九河云将帮助您排查和解决在创建AWS账户时可能遇到的一些常见问题,包括未接到验证电话、最大失败尝试次数错误以及账户激活延迟等。 常见问题及解决方法 1. …...
Spark-RDD持久化
一、Spark的三种持久化机制 1、cache 它是persist的一种简化方式,作用是将RDD缓存到内存中,以便后续快速访问,提高计算效率。cache操作是懒执行的,即执行action算子时才会触发。 2、persist 它提供了不同的存储级别࿰…...
vue2中使用tailwindCss 详细教程
1、先看官方文档:https://www.tailwindcss.cn/ 2、先安装:npm install -D tailwindcss ---------------通过 npm 安装 tailwindcss,然后创建你自己的 create your tailwind.config.js 配置文件。 npm install -D tailwindcss 3、初始化文件—npx tailwindcss init npx ta…...
机器视觉工程师一直做调试,维护岗位,想转岗软件方面C#从零开始,快则三年不到,慢则一辈子不会
其实不是每一家做视觉检测,或者是做设备必须要机器视觉工程师开发,其实公司对标准软件更感兴趣,主要非常高的性价比,省时省钱省人。所以这里有个问题,就是公司平台的重要性,首先他对开发是刚需,…...
【初阶数据结构】详解二叉树 - 树和二叉树(三)(递归的魅力时刻)
文章目录 前言1. 二叉树链式结构的意义2. 手搓一棵二叉树3. 二叉树的遍历(重要)3.1 遍历的规则3.2 先序遍历3.3 中序遍历3.4 后序遍历3.5 遍历的代码实现3.5.1 先序遍历代码实现3.5.2 中序遍历代码实现3.5.3 后序遍历代码实现 4. 统计二叉树结点的个数5.…...
【QT】QWidget 重要属性
文章目录 enabledgeometrywindowTitlewindowIconqrc 机制windowOpacitycursorfontQFont toolTip 和 toolTipDurationfocusPolicyQt::FocusPolicy styleSheet enabled 作用:设置控件是否可使用. true 表⽰可用, false 表⽰禁用. 对应的API bool isEnabled(); // 获…...
什么是数据库连接池?为什么需要使用连接池?
什么是数据库连接池?为什么需要使用连接池? 什么是数据库连接池? 数据库连接池是一种创建和管理数据库连接的技术。在传统的应用程序中,每当需要与数据库进行交互时,都会创建一个新的数据库连接。 这种做法虽然简单…...
2024ICPC网络赛第一场C. Permutation Counting 4(线性代数)
题目链接 题目大意:给你n个范围[ l i , r i l_i,r_i li,ri],每个位置可以在这个范围中选择一个数,然后形成排列1到n的排列p。问p的所有情况的个数的奇偶性。 一个很妙的行列式转化,纯纯的线性代数。 首先,我们把…...
01.前端面试题之ts:说说如何在Vue项目中应用TypeScript?
文章目录 一、前言二、使用Componentcomputed、data、methodspropswatchemit 三 、总结 一、前言 与link类似 在VUE项目中应用typescript,我们需要引入一个库vue-property-decorator, 其是基于vue-class-component库而来,这个库vue官方推出…...
【HTTP】方法(method)以及 GET 和 POST 的区别
文章目录 方法(method)登录上传GET 和 POST 有什么区别(面试)区别不准确的说法 方法(method) 首行中的第一部分。首行是由方法、URL 和版本号组成 方法描述了这次请求想干什么,最主要的是&…...
Ubuntu NFS 搭建及配置
在 Ubuntu 上搭建和配置 NFS(Network File System)服务器,可以让其他设备通过网络访问共享的文件夹。以下是步骤指南: 1. 安装 NFS 服务器 首先,安装 NFS 服务器软件包: sudo apt update sudo apt insta…...
双十一好物推荐,这些值得入手的宝藏产品
随着双十一的钟声即将敲响,这个万众期待的购物盛宴就要来临!为了让大家避免在众多的商品中不知所措,妮妮精心筹备了一份购物清单,分享那些我亲身感受超棒,觉得十分值得购买的物品。 这些商品不但价格合理,而…...
秋招内推2025--招联金融
【投递方式】 直接扫下方二维码,或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus,使用内推码 igcefb 投递) 【招聘岗位】 后台开发 前端开发 数据开发 数据运营 算法开发 技术运维 软件测试 产品策…...
C++类和对象——第二关
目录 类的默认成员函数: (一)构造函数 (二)析构函数 (三)拷贝构造函数 类的默认成员函数: 类里面有6个特殊的成员函数分别包揽不同的功能; (一)构造函数…...
服务器数据恢复—raid5阵列热备盘上线失败导致阵列崩溃的数据恢复案例
服务器磁盘阵列数据恢复环境: 服务器中有两组分别由4块SAS硬盘组建的raid5磁盘阵列,两组raid5阵列划分LUN,组成LVM结构,格式化为EXT3文件系统。 服务器磁盘阵列故障: 服务器中一组raid5阵列中有一块硬盘离线ÿ…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
