DS并查集(17)

文章目录

  • 前言
  • 一、何为并查集?
  • 二、并查集的实现?
    • 并查集的初始化
    • 查找元素所在的集合
    • 判断两个元素是否在同一个集合
    • 合并两个元素所在的集合
    • 获取并查集中集合的个数
    • 并查集的路径压缩
  • 三、来两道题练练手?
    • 省份的数量
    • 等式方程的可满足性
  • 总结


前言

  其实我一开始是想直接讲图的,但是但是考虑的图的 Kruskal 算法要用到,就先讲解下并查集吧!


一、何为并查集?

  并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题
  并查集通常用森林来表示,森林中的每棵树表示一个集合,树中的结点对应一个元素

这可能太抽象了,我们举个具体的例子:

  以朋友圈为例,现在有10个人(从0开始编号),刚开始这10个人互不认识,所以各自属于一个集合
在这里插入图片描述
  并查集会用一个数组来表示这10个人之间的关系,数组的下标对应就是这10个人的编号,刚开始时数组中的元素都初始化为-1
在这里插入图片描述

数组中某个位置的值为负数,表示该位置是树的根,这个负数的绝对值表示的这棵树(集合)中数据的个数,因为刚开始每个人各自属于一个集合,所以将数组中的位置都初始化为-1

  后来这10个人之间通过相互认识,最终形成了三个朋友圈
在这里插入图片描述
  此时并查集数组中各个位置的值如下
在这里插入图片描述

数组中某个位置的值为非负数,表示该位置不是树的根,这个非负数的值就是这个结点的父结点的编号

  后来4号和8号又通过某种机遇互相认识了,这时他们所在的两个集合就需要进行合并,最终就变成了两个朋友圈

在这里插入图片描述
  需要注意的是,在根据两个元素合并两个集合时,需要先分别找到这两个元素所在集合的根结点,然后再将一个集合合并到另一个集合,并且合并后需要更新数组中根结点的值

在这里插入图片描述

为什么要找根节点?

  1. 如果这两个元素所在集合的根结点相同,说明这两个元素本身就在同一个集合,无需合并
  2. 合并集合后需要更新这两个集合的根结点的值

  而要判断两个元素是否在同一个集合,也就是判断这两个元素所在集合的根结点是否相同

二、并查集的实现?

  首先我们要想元素的下标是否对应,如果无法对应的话,我们一般利用容器 map 来存储元素的下标映射

template<class T>
class UnionFindSet
{
public:UnionFindSet(const T* a, size_t n){for (size_t i = 0 ; i < n ; i++) {_a.push_back(a[i]);_indexMap[a[i]] = i;}}
private:vector<T> _a; // 编号找人map<T, int> _indexMap;// 人找编号
};

  不过在这里,我们假设给的就是下标,也就是说私有成员就只有一个 vector< int > _ufs

并查集的初始化

  并查集中会用一个数组来维护各个结点之间的关系,在初始化并查集时,根据元素的个数开辟数组空间,并将数组中的元素初始化为 -1 即可

//构造函数
UnionFindSet(int n):_ufs(n, -1) //初始时各个元素自成一个集合
{}

查找元素所在的集合

查找逻辑如下:

  • 如果元素对应下标位置存储的是负数,则说明该元素即为根结点,返回该元素即可。
  • 如果元素对应下标位置存储的是非负数,则跳转到其父结点的位置继续查找根结点。
    int FindRoot(int x){int root = x;while (_ufs[root] >= 0){root = _ufs[root];}return root;}

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

    bool isInset(int x1, int x2){return FindRoot(x1) == FindRoot(x2);}

合并两个元素所在的集合

合并逻辑如下:

  • 分别找到两个元素所在集合的根结点。
  • 如果这两个元素所在集合的根结点相同,则无需合并,如果这两个元素所在集合的根结点不同,则将小集合合并到大集合上。
  • 将小集合根结点的值累加到大集合的根结点上,使得大集合根结点的值的绝对值等于两个集合中元素的总数。
  • 将小集合根结点的值改为大集合根结点的编号,也就是让小集合的根结点作为大集合根结点的孩子,使得两个集合变为一个集合。
    // 合并两个集合void Union(int x1, int x2){int root1 = FindRoot(x1);int root2 = FindRoot(x2);// 如果本身就在一个集合,那么无需合并if (root1 == root2) return ;// 合并的时候,作为孩子,其深度会增加一层// 因此,理论上来说,数据量小的作孩子// 如果有需求下标小的作根,则交换一下root// if (root1 > root2) swap(root1, root2); // 按照下标大小if (abs(_ufs[root1]) < abs(_ufs[root2])) swap(root1, root2); // 按照数据量大小// 如果不在同一个集合,则默认认为把x2代表集合合并到x1上,即x1作根,x2作子_ufs[root1] += _ufs[root2];// 将x2代表集合的名称改为x1_ufs[root2] = root1;}

获取并查集中集合的个数

  要获取并查集中集合的个数,本质就是统计数组中负值(根结点)的个数

    size_t SetSize(){size_t size = 0;for (size_t i = 0 ; i < _ufs.size() ; i++){if (_ufs[i] < 0) size++;}return size;}

并查集的路径压缩

  当数据量很大的时候,并查集中树的层数可能会变得很高,这时再查找一个元素所在集合的根结点时就需要往上走很多层,这时可以考虑进行路径压缩

  路径压缩一般会在查找根结点时进行,当根据一个结点查找其根结点时,该路径上所有的结点都会被压缩,最终这些结点会直接被挂在根结点下,下次再根据这些结点查找根结点时就能快速找到根结点

    int FindRoot(int x){int root = x;while (_ufs[root] >= 0){root = _ufs[root];}// 路径压缩,用于应对深度太深的情况// 把路径上所有节点都作为根的孩子while (_ufs[x] >= 0){int parent = _ufs[x];_ufs[x] = root;x = parent;}return root;}

三、来两道题练练手?

省份的数量

LCR 116. 省份数量

在这里插入图片描述
思路其实是很明确的:

  1. 定义一个长度为 n 的数组充当并查集,并将数组中的元素初始化为 -1,表示各个城市各自是一个省份。
  2. 根据所给矩阵,对并查集中的各个集合进行合并。
  3. 并查集中集合的个数即为省份的数量
class Solution
{
public:int findCircleNum(vector<vector<int>>& isConnected){UnionFindSet ufs(isConnected.size());for (size_t i = 0 ; i < isConnected.size() ; i++){for (size_t j = 0 ; j < isConnected[i].size() ; j++){if (isConnected[i][j]){ufs.Union(i, j);}}}return ufs.SetSize();}};

等式方程的可满足性

990. 等式方程的可满足性

在这里插入图片描述
思路其实也是很明确的:

  1. 定义一个长度为26(变量为小写字母)的数组充当并查集,并将数组中的元素初始化为-1,表示各个字母只有自己等于自己。
  2. 根据字符串方程组中的等式,对并查集中的各个集合进行合并(每个集合中的元素都是相等的)。
  3. 根据并查集,对字符串方程组中的不等式进行验证,如果两个不相等的变量出现在同一个集合中,则返回 false 。
class Solution
{
public:bool equationsPossible(vector<string>& equations){UnionFindSet ufs(26);for (const auto& str : equations){if (str[1] == '='){ufs.Union(str[0] - 'a', str[3] - 'a');}}for (const auto& str : equations){if (str[1] == '!'){int root1 = ufs.FindRoot(str[0] - 'a');int root2 = ufs.FindRoot(str[3] - 'a');if (root1 == root2) return false;}}return true;}
};

总结

  其实,从这里开始的数据结构就比较困难了
  图、跳表、B树,它们要来了!!!

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

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

相关文章

程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<2>

大家好啊&#xff0c;我是小象٩(๑ω๑)۶ 我的博客&#xff1a;Xiao Xiangζั͡ޓއއ 很高兴见到大家&#xff0c;希望能够和大家一起交流学习&#xff0c;共同进步。 今天我们来学习const修饰指针&#xff0c;包括const修饰变量&#xff0c;const修饰指针变量&#xff1b…

DeepSeek 云端部署,释放无限 AI 潜力!

1.简介 目前&#xff0c;OpenAI、Anthropic、Google 等公司的大型语言模型&#xff08;LLM&#xff09;已广泛应用于商业和私人领域。自 ChatGPT 推出以来&#xff0c;与 AI 的对话变得司空见惯&#xff0c;对我而言没有 LLM 几乎无法工作。 国产模型「DeepSeek-R1」的性能与…

小程序的数据绑定与事件绑定

1.数据绑定的基本原则 2.在data中定义页面的数据 3.Mustache语法的格式 &#xff08;其实可以把他理解为插值表达式&#xff09; 动态绑定属性 三元运算 算数运算 4.事件绑定 事件绑定基本使用

实验一---典型环节及其阶跃响应---自动控制原理实验课

一 实验目的 1.掌握典型环节阶跃响应分析的基本原理和一般方法。 2. 掌握MATLAB编程分析阶跃响应方法。 二 实验仪器 1. 计算机 2. MATLAB软件 三 实验内容及步骤 利用MATLAB中Simulink模块构建下述典型一阶系统的模拟电路并测量其在阶跃响应。 1.比例环节的模拟电路 提…

Git进阶之旅:Git 多人合作

项目克隆&#xff1a; git clone 仓库地址&#xff1a;把远程项目克隆到本地形成一个本地的仓库 克隆下来的仓库和远程仓库的名称一致 注意&#xff1a;git clone 远程仓库地址 远程仓库名&#xff1a;把远程仓库克隆下来&#xff0c;并自定义仓库名 多人协作&#xff1a; …

Baklib赋能企业实现高效数字化内容管理提升竞争力

内容概要 在数字经济的浪潮下&#xff0c;企业面临着前所未有的机遇与挑战。随着信息技术的迅猛发展&#xff0c;各行业都在加速推进数字化转型&#xff0c;以保持竞争力。在这个过程中&#xff0c;数字化内容管理成为不可或缺的一环。高效的内容管理不仅能够优化内部流程&…

【C++ 数学 括号匹配】2116. 判断一个括号字符串是否有效|2037

本文涉及知识点 数学 括号匹配 LeetCode2116. 判断一个括号字符串是否有效 一个括号字符串是只由 ‘(’ 和 ‘)’ 组成的 非空 字符串。如果一个字符串满足下面 任意 一个条件&#xff0c;那么它就是有效的&#xff1a; 字符串为 (). 它可以表示为 AB&#xff08;A 与 B 连接…

计算机毕业设计Python+CNN卷积神经网络考研院校推荐系统 考研分数线预测 考研推荐系统 考研爬虫 考研大数据 Hadoop 大数据毕设 机器学习

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

仿真设计|基于51单片机的温室环境监测调节系统

目录 具体实现功能 设计介绍 51单片机简介 资料内容 仿真实现&#xff08;protues8.7&#xff09; 程序&#xff08;Keil5&#xff09; 全部内容 资料获取 具体实现功能 &#xff08;1&#xff09;LCD1602液晶第一行显示当前的光照值及二氧化碳浓度值&#xff0c;第二…

智慧园区如何利用智能化手段提升居民幸福感与环境可持续性

内容概要 在当今社会&#xff0c;随着城市化进程的加快&#xff0c;智慧园区作为一种新兴的城市管理模式&#xff0c;逐渐获得了人们的关注。智慧园区不仅仅是物理空间的规划&#xff0c;更是一种通过智能化手段提升居民幸福感与环境可持续性的综合解决方案。本段将对智慧园区…

Android --- CameraX讲解

预备知识 surface surfaceView SurfaceHolder surface 是什么&#xff1f; 一句话来说&#xff1a; surface是一块用于填充图像数据的内存。 surfaceView 是什么&#xff1f; 它是一个显示surface 的View。 在app中仍在 ViewHierachy 中&#xff0c;但在wms 中可以理解为…

NLP深度学习 DAY5:Sequence-to-sequence 模型详解

Seq2Seq&#xff08;Sequence-to-Sequence&#xff09;模型是一种用于处理输入和输出均为序列任务的深度学习模型。它最初被设计用于机器翻译&#xff0c;但后来广泛应用于其他任务&#xff0c;如文本摘要、对话系统、语音识别、问答系统等。 核心思想 Seq2Seq 模型的目标是将…

Java锁自定义实现到aqs的理解

专栏系列文章地址&#xff1a;https://blog.csdn.net/qq_26437925/article/details/145290162 本文目标&#xff1a; 理解锁&#xff0c;能自定义实现锁通过自定义锁的实现复习Thread和Object的相关方法开始尝试理解Aqs, 这样后续基于Aqs的的各种实现将能更好的理解 目录 锁的…

基于STM32的阿里云智能农业大棚

目录 前言&#xff1a; 项目效果演示&#xff1a; 一、简介 二、硬件需求准备 三、硬件框图 四、CubeMX配置 4.1、按键、蜂鸣器GPIO口配置 4.2、ADC输入配置 4.3、IIC——驱动OLED 4.4、DHT11温湿度读取 4.5、PWM配置——光照灯、水泵、风扇 4.6、串口——esp8266模…

【游戏设计原理】96 - 成就感

成就感是玩家体验的核心&#xff0c;它来自完成一件让自己满意的任务&#xff0c;而这种任务通常需要一定的努力和挑战。游戏设计师的目标是通过合理设计任务&#xff0c;不断为玩家提供成就感&#xff0c;保持他们的参与热情。 ARCS行为模式&#xff08;注意力、关联性、自信…

MySQL CTE:解锁SQL查询新模式

目录 一、CTE 初相识 二、CTE 基础语法 &#xff08;一&#xff09;基本语法结构 &#xff08;二&#xff09;语法规则详解 三、非递归 CTE 应用实例 &#xff08;一&#xff09;单 CTE 简单查询 &#xff08;二&#xff09;多 CTE 联合查询 四、递归 CTE 深入探索 &…

C#,入门教程(12)——数组及数组使用的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(11)——枚举&#xff08;Enum&#xff09;的基础知识和高级应用https://blog.csdn.net/beijinghorn/article/details/123917587https://blog.csdn.net/beijinghorn/article/details/123917587 数组是一种数据集合&#xff0c;是一组…

【leetcode练习·二叉树】计算完全二叉树的节点数

本文参考labuladong算法笔记[拓展&#xff1a;如何计算完全二叉树的节点数 | labuladong 的算法笔记] 如果让你数一下一棵普通二叉树有多少个节点&#xff0c;这很简单&#xff0c;只要在二叉树的遍历框架上加一点代码就行了。 但是&#xff0c;力扣第第 222 题「完全二叉树的…

低代码系统-产品架构案例介绍、轻流(九)

轻流低代码产品定位为零代码产品&#xff0c;试图通过搭建来降低企业成本&#xff0c;提升业务上线效率。 依旧是从下至上&#xff0c;从左至右的顺序 名词概述运维层底层系统运维层&#xff0c;例如上线、部署等基础服务体系内置的系统能力&#xff0c;发消息、组织和权限是必…

对顾客行为的数据分析:融入2+1链动模式、AI智能名片与S2B2C商城小程序的新视角

摘要&#xff1a;随着互联网技术的飞速发展&#xff0c;企业与顾客之间的交互方式变得日益多样化&#xff0c;移动设备、社交媒体、门店、电子商务网站等交互点应运而生。这些交互点不仅为顾客提供了便捷的服务体验&#xff0c;同时也为企业积累了大量的顾客行为数据。本文旨在…