算法.图论-并查集

文章目录

    • 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;}
}

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

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

相关文章

SentencePiece进行文本分类

SentencePieces 前言 Step1:故事 SentencePiece 是一个无监督的文本分词器和 detokenizer(还原回去的&#xff1f;)主要用于词汇表大小是预定的文本生成系统中它拓展了原始句子的训练&#xff0c;实现子词单元如 BPE 和 unigram language model技术亮点 纯数据驱动&#xff…

数据结构:双指针—移动0(OJ283)

给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0]示例 2: 输入: nums [0] 输出: […

Azure Kinect 人体跟踪关节

Azure Kinect 人体跟踪关节 azure kinect dk 提取人体骨骼 要在Azure Kinect DK上提取人体骨骼&#xff0c;你需要使用Azure Kinect SDK和OpenPose库。以下是一个简化的代码示例&#xff0c;展示如何集成这两个库来提取骨骼关键点&#xff1a; 首先&#xff0c;确保你已经安装…

Web3Auth 如何工作?

Web3Auth 用作钱包基础设施&#xff0c;为去中心化应用程序 (dApp) 和区块链钱包提供增强的灵活性和安全性。在本文档中&#xff0c;我们将探索 Web3Auth 的功能&#xff0c;展示它如何为每个用户和应用程序生成唯一的加密密钥提供程序。 高级架构 Web3Auth SDK 完全存在于用…

软件测试基础篇

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 “尽早的介入测试&#xff0c;遇到问题的解决成本就越低” 随着软件测试技术的发展&#xff0c;测试工作由原来单一的寻找缺陷逐渐发展成为预防缺陷&#xff0c;…

(含答案)C++笔试题你可以答对多少?

1.每个C程序中都必须包含的函数名&#xff1f;&#xff08;A&#xff09; A.main C.Name B.MAIN D.class 2.没有使用private关键字定义类的数据成员&#xff0c;则默认为&#xff1f;&#xff08;A&#xff09; A.private C.protected B.public D.friend 3.int Fun…

c# 将调试信息到VS输出窗口

在C#中&#xff0c;有多种方式可以在输出窗口输出信息&#xff0c;具体取决于你使用的开发环境和目标平台。以下是几种常见的方法&#xff1a; 1. 使用 Console.WriteLine 如果你在控制台应用程序中&#xff0c;可以使用 Console.WriteLine 方法将信息输出到控制台窗口。 us…

文章解析: 一不小心掉入了 Java Interface 的陷阱

一不小心掉入了 Java Interface 的陷阱_腾讯新闻 import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.Iterator; import java.util.List;// 方便起见就都放在一个文件中了 public class TestSimpleResult {public static void ma…

Rust和Go谁会更胜一筹

在国内&#xff0c;我认为Go语言会成为未来的主流&#xff0c;因为国内程序员号称码农&#xff0c;比较适合搬砖&#xff0c;而Rust对心智要求太高了&#xff0c;不适合搬砖。 就个人经验来看&#xff0c;Go语言简单&#xff0c;下限低&#xff0c;没有什么心智成本&#xff0c…

华为认证HCIA篇--网络通信基础

大家好呀&#xff01;我是reload。今天来带大家学习一下华为认证ia篇的网络通信基础部分&#xff0c;偏重一些基础的认识和概念性的东西。如果对网络通信熟悉的小伙伴可以选择跳过&#xff0c;如果是新手或小白的话建议还是看一看&#xff0c;先有个印象&#xff0c;好为后续的…

安卓Settings值原理源码剖析存储最大的字符数量是多少?

背景&#xff1a; 平常做rom相关开发时候经常需要与settings值打交道&#xff0c;需要独立或者存储一个settings的场景&#xff0c;群里有个学员朋友就问了一个疑问&#xff0c;那就是Settings的putString方式来存储字符&#xff0c;那么可以存储的最大字符是多少呢&#xff1…

Excel锁定单元格,使其不可再编辑

‌在Excel中&#xff0c;锁定单元格后仍然可以编辑‌&#xff0c;这主要涉及到对特定单元格或区域的锁定与保护工作表的设置。以下是实现这一功能的具体步骤&#xff1a; ‌解除工作表的锁定状态‌&#xff1a;首先&#xff0c;需要全选表格&#xff08;使用CtrlA快捷键&#x…

[数据集][目标检测]中草药类型识别检测数据集VOC+YOLO格式7976张45类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;7976 标注数量(xml文件个数)&#xff1a;7976 标注数量(txt文件个数)&#xff1a;7976 标注…

2024新动态:低代码开发占领新常态市场

随着技术的不断进步和数字化转型的加速&#xff0c;企业对于快速开发和部署应用程序的需求日益增长。2024年&#xff0c;低代码开发平台已经成为新常态市场的重要力量&#xff0c;它通过简化应用程序的开发过程&#xff0c;让非技术背景的业务用户也能参与到软件开发中来&#…

【C++】哈希桶

前言 哈希桶是哈希表中用于存储数据的基本单元&#xff0c;也称为哈希槽或存储桶。 哈希桶&#xff08;Hash Bucket&#xff09;** 是哈希表数据结构中的一个概念。、哈希表通过哈希函数将输入数据映射到一个存储位置&#xff0c;而哈希桶就是这些存储位置中的一个单元。哈希桶…

WPF入门教学六 Grid布局进阶

在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;Grid布局是一种非常强大且灵活的布局控件&#xff0c;它允许你创建复杂的用户界面。以下是Grid布局的一些进阶技巧和教学&#xff1a; 一、基本概念回顾 Grid定义&#xff1a;Grid是一个用于布局…

深度学习(6):Dataset 和 DataLoader

文章目录 Dataset 类DataLoader 类 Dataset 类 概念&#xff1a; Dataset 是一个抽象类&#xff0c;用于表示数据集。它定义了如何获取数据集中的单个样本和标签。 作用&#xff1a; 为数据集提供统一的接口&#xff0c;便于数据的读取、预处理和管理。 关键方法&#xff…

AI公司的妄念:招个AI产品经理来想idea

AI公司在探索方向时&#xff0c;一旦老板或负责人的想法陷入瓶颈&#xff08;或没时间想特别细分的方向&#xff09;&#xff0c;往往会希望招一个AI产品经理来想idea&#xff08;创新/探索新方向&#xff09;&#xff0c;预期他某天突然想出个特别好的idea。 一、这个思路&…

【机器学习】12-决策树1——概念、特征选择

机器学习10-决策树1 学习样本的特征&#xff0c;将样本划分到不同的类别&#xff08;分类问题&#xff09;或预测连续的数值&#xff08;回归问题&#xff09;。 选择特征&#xff0c;划分数据集&#xff0c;划分完成形成模型&#xff08;树结构&#xff09;&#xff0c;一个…

OSI 七层模型和TCP/IP 四层模型的区别

目录 OSI 七层模型 介绍 1. 物理层&#xff08;Physical Layer&#xff09; 2. 数据链路层&#xff08;Data Link Layer&#xff09; 3. 网络层&#xff08;Network Layer&#xff09; 4. 传输层&#xff08;Transport Layer&#xff09; 5. 会话层&#xff08;Session …