数据结构--并查集

2023-11-05

并查集适用情况

1、有时候,并不关心数据之间的前后关系,也不关心数据的层次关系。一些确定元素只是单纯的聚集在一起,这样的元素聚集集合被称为集合
当希望知道某个数据是否存在一个集合中,或者两个元素是否在同一个集合中时,就需要使用一些集合数据结构来维护聚合元素之间的关系。如并查集、Hash表等

2、在一些集合的应用问题中,常给出集合中各个元素之间直接或间接的联系,要求将这些元素分成几个集合,并要求反复查找一个元素在哪个集合中。问题看似简单,但由于这类问题一般数据规模较大,采用常规的实现方法在空间和时间上都难以达到信息学竞赛的要求,而解决这类问题的一种有效的方法就是使用并查集。

3、有了树结构的基础知识后,我们来思考这样一个问题。
有很多棵树,每棵树有自己的节点,这些树组成了一个森林。
我们进行多次询问,每次询问两个节点是否在同一棵树上。这个问题应该如何处理呢?
在这里插入图片描述

例如:我们询问 (20,21) 是否在同一棵树上?

我们可能会对每棵树做一个编号,然后对树进行 DFS ,为树的每个节点进行编号,之后处理每个询问时,我们只要比较 2 个节点的编号是否相同即可

如果在询问的过程中,我们还会对一些树进行合并,合并后重新询问。这个问题又该如何处理呢?

树的合并问题。我们可以暴力的把一棵树中所有节点的编号改成另一棵树的编号。

这个方法最坏情况的复杂度是 O(n2) ,即每次都将一个较大的树合并到较小的树上。
如果我们规定,每次将较小的树合并到较大的树上,那么算法的复杂度为 O(nlog(n))
,这个方法也叫做启发式合并,未来在其他结构的合并中也会用到。

并查集的路径压缩

并查集顾名思义,包括并和查 2 个部分,并( union )即合并两个集合(树),查( search )检查 2 个点是否在同一个集合(树)内。

实际上是对上述问题的路径压缩策略。
在这里插入图片描述
我们不需要做任何的预处理,每次查询的时候,我们从 2 个节点分别向他们的根结点走,如果最终根节点是同一个,则表明两个节点在同一棵树中,否则是不同的两棵树。
例如: 20 的根为 1 , 21 的根为 2 ,所以不在同一棵树中。
由于这类查询的特殊性,我们对树的存储方式进行一下修改,不用保存每个节点有哪些子节点,转为只保存每个节点的父节点是谁,这样并不影响上面的查询过程,并且更为灵活。
在大部分情况下,从节点走到根是很快的。但存在极为特殊的情况,也就是说这棵树退化为一个链表,这样从节点到根的距离,最坏就是 n 。如果我们重复多次对这 2 个节点进行查询,那么算法是非常低效的。
既然是多次对同一对节点进行查询,我们是否可以把之前的结果记录下来呢?这样下次询问时就不必重走一遍,直接给出结果就好。简单来讲,我们直接将节点的父节点设为他的根节点即可,虽然改变了树的形态,但并不影响查询结果。
在这里插入图片描述
例如:将 20 的父节点设为 1 , 21 的父节点设为 2
我们考虑对上面的方法做进一步的优化。除了将节点自己的父节点设为根节点,所有从节点到根的路径上的节点,我们都可以使用同样的方法,将他们的父节点设为根结点。
在这里插入图片描述
例如:将 20,16,10,4 的父节点设为 1,21,17,13,7 的父节点设为 2 。
我们将这个过程称为路径压缩。路径压缩有效的提高了并查集的效率。

并查集的按秩合并

那么如何解决树的合并呢?这个其实很简单,我们只需要将原来某棵树的根节点,修改为另一棵树的根节点即可。这样合并也是 O(1) 的。
以上就是带路径压缩的并查集的过程,下面动图演示了合并 0,7 两点的过程。
在这里插入图片描述
并查集的复杂度低于 O(nlog(n)) ,这部分内容我们会在后面继续讲解。
除了路径压缩,并查集还有一个重要的约束:按秩合并。
那么什么是并查集的按秩合并呢?
该方法使用秩( rank )来表示树高度的上界,在合并时,总是将具有较小秩的树根指向具有较大秩的树根。简单的说,就是总是将 rank 值小的作为子树,添加到 rank 值大的树中(这个 rank 可以近似看为树的高度)
如果我们在合并两棵树时没有任何限制,在某些数据下,并查集的复杂度很有可能降到 O(nlog(n))
例如下图这种情况:
在这里插入图片描述
第一次合并大的集合和绿色这个点,之后查询红点,红点路径的长度为 O(log(n)) ,所以查询一次的复杂度为O(log(n)) ,而路径压缩之后整棵树又变成左下角的结构,如果再一次和一个绿点合并之后再查询红点,复杂度还是O(log(n)) 的,这就导致产生一个循环,用这样的合并和查询就可能导致不按秩合并的并查集的复杂度降到O(nlog(n)) 。
并查集是我们解决题目过程中常用的数据结构,通常用来解决连通性判定问题。最后给出带按秩合并的并查集的完整模板。
在这里插入图片描述

并查集的时间复杂度和基本操作

(1)初始化
把每个点所在集合初始化为其自身。通常来说,这个步骤在每次使用该数据结构时只需要执行一次,时间复杂度均为O(N)。
(2)查找
查找元素所在的集合,即根节点。对于判断两个元素是否属于同一个集合,它也是非常有用的。
(3)合并
将两个元素所在的集合合并为一个集合。通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的“查找”操作实现。

(4)时间复杂度
通过按秩合并和路径压缩两个操作,并查集的复杂度可以达到O(Alpha(n)) , 在这里,Alpha(n) 是阿克曼(Ackermann)函数的反函数。这比O(log(n)) 还要快。
不过,这是“均摊复杂度”。也就是说,并不是每一次操作都满足这个复杂度,而是多次操作之后平均每一次操作的复杂度是O(Alpha(n)) 的意思。

例题1:亲戚
实现程序代码:
(1)初始化并查集

 for(int i=1;i<=n;i++) f[i]=i; //f[i]存储i的父结点

(2)查找根结点
① 递归代码:

    int find(int x) //查找x元素所属集合的根结点
{
   if(x!=f[x]) return find(x);
   else        return x;
}

② 非递归代码:

int find(x)
{
   while(x!=f[x]) x=f[x];
   return x;
}

(3)合并两个集合

 void merge(int x,int y)
{
    int r1=find(x);   //查找x元素的所在集合的根结点
    int r2=find(y);   //查找y元素的所在集合的根结点
    if(r1!=r2)  f[r1]=r2; //将集合r1合并到集合r2上
}

(4)路径压缩代码:

int find(int x) //查找x元素所属集合的根结点
{
   if(x!=f[x]) f[x]=find(f[x]);
   return f[x];
}

完整代码

#include<bits/stdc++.h>
using namespace std;
int f[5001],n,m,p,x,y; 
int find(int i)
{
    if(i==f[i]) return i;
    else         return f[i]=find(f[i]); //路径压缩 
        
}
int main()
{
  cin>>n>>m>>p;
  for(int i=1;i<=n;i++) f[i]=i; //初始每个点是一个独立的集合,根结点是本身 
  for(int i=1;i<=m;i++)
  {
     cin>>x>>y;
     int g1=find(x); //查找元素x的根结点
     int g2=find(y); //查找元素y的根结点
     if(g1!=g2) f[g1]=g2; //合并两个集合
  }
  for(int i=1;i<=p;i++)
     {
        cin>>x>>y;
        int g1=find(x);
        int g2=find(y);
        if(g1==g2) cout<<"Yes"<<endl;
        else       cout<<"No"<<endl;          
     }        
return 0;    
}

并查集应用

并查集常作为另一种复杂的数据结构或者算法的存储结构。常见的应用有:求无向图的连通分量个数,最近公共祖先(LCA),带限制的作业排序,实现Kruskar算法求最小生成树等。
应用:
1、求无向图连通分量
2、判断两个点是否在一个连通分量里(kruskal)
它在计算机科学中有着广泛的应用,例如,迷宫的生成、确定无向图的连通子图个数、求解最小生成树、亲戚关系的判定、最小公共祖先等等问题,无一例外都用到了并查集。

练习题目:

1、修建传送门
2、摧毁数组

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构--并查集 的相关文章

随机推荐

  • 数值优化(Numerical Optimization)学习系列-惩罚和增广拉格朗日方法(Augmented Lagrangian Methods)

    概述 求解带约束的最优化问题 一类很重要的方法就是将约束添加到目标函数中 从而转换为一系列子问题进行求解 最终逼近最优解 关键问题是如何将约束进行转换 本节主要介绍 1 二次惩罚方法 2 非平滑惩罚方法 3 增广拉格朗日方法 二次惩罚方法
  • c++ 实现智能指针shared_ptr

    sharedPtr h ifndef sharedPtr H define sharedPtr H class sharedPtr public sharedPtr sharedPtr int sharedPtr const sharedP
  • 八大排序算法(原理+代码详解)Python版

    一 前言 排序算法是最经典的算法知识 往往面试题中或数据结构中会涉及有关排序的算法 掌握排序算法的思想及其原理有助于化解排序方面的难题 下面介绍几种Python语言中常见的排序算法 冒泡排序 选择排序 插入排序 归并排序 快速排序 希尔排序
  • 推荐的自动标注工具

    之前研究了Android AutoLayout的使用 不过项目开发过程中提供的设计图往往没有标注完整的UI 这时候需要开发工程师自己搞定了 于是搜索并尝试了一下 找到一些方便的自动标注工具 同时作下记录 方便后来者借鉴与使用 一 一套免费的
  • DocX 生成Word

    当然 这里是一个使用DocX库在 NET Core中操作Word文档的简单示例 首先 确保你在项目中安装了DocX库 你可以在NuGet包管理器中搜索并安装DocX 然后 使用以下代码来创建一个简单的Word文档并添加一些内容 using
  • 有关Centos7的网络配置问题(桥接模式)

    在经过了NAT模式配置的多重灾难后 本小白得知 桥接模式还可以ping通主机 于是做了一个大胆的决定 转为桥接模式 接下来记录一下我的过程 PS 指路 NAT模式下网络配置 1 打开网络适配器 禁用两块虚拟网卡 2 打开VMware Wor
  • springboot+mysql汉服销售系统-计算机毕业设计源码95171

    目 录 摘要 1 绪论 1 1开发背景 1 2国内外研究慨况 1 3springboot框架介绍 1 4论文结构与章节安排 2 Springboot汉服销售系统小程序系统分析 2 1 可行性分析 2 1 1 技术可行性分析 2 1 2 经济
  • Java基本知识之运算符

    算数运算符 注意一下这个 运算类型 结果 a 2 b a a 3 b 3 a 3 b a a 3 b 2 数字 先自增1 后运算 数字 先运算 后自增1 public class Hello public static void main
  • 一文带你了解Flutter如何内存优化

    在Flutter应用程序中 优化内存管理是提高应用程序性能和稳定性的关键 本文介绍了如何优化Flutter应用程序的内存管理 包括理解Flutter的内存管理机制 使用内存分析工具 减少不必要的对象创建 优化图片加载 避免使用过多的动画和效
  • MySQL组合索引提升查询速度实战

    1 问题描述 生产环境后台管理查询司机钱包汇总列表及统计所有司机钱包收入和支出金额 不管是查询一天还是一个月的速度都比较慢 经常会超时 超过两分钟未响应结果 2 问题排查 通过排查发现查询时的两张表数据时间字段均是以日期为单位 而每张表中的
  • 智能机器人教具法则

    对于智能机器人教育 国内政策不断落地 新生代父母增加 教育理念和教育水平提高 儿童综合素质培养的关注度越来越高 在教育观念升级的环境下 相比于被电子屏幕占据大部分的时间 格物斯坦希望为孩子们找到游戏和教育之间的平衡点 所以 寓教于乐 逐渐成
  • 【软件工程基础复习整理】第三章项目计划(1)概述与风险分析

    软件项目计划 一年之计在于春 一日之计在于寅 增广贤文 谋于前才可捕获于后 临大事而不乱 苏轼 如果软件项目值得开发 能够开发 我们要制定项目计划 对资源成本框架进行合理的调度 软件项目的失败大多数是因为计划不周引起的 计划对项目的成败有关
  • 1200*A. You‘re Given a String...(枚举)

    include
  • 安卓前端 UI框架

    框架大全 http www oschina net project tag 342 android ui 前言 忙碌的工作终于可以停息一段时间了 最近突然有一个想法 就是自己写一个app 所以找了一些合适开源控件 这样更加省时 再此分享给大
  • JSch-用java实现服务器远程操作

    介绍 前段时间接了一个比较特殊的需求 需要做一个用于部署服务的服务 主要是将一个k8s服务集群部署到远端的服务器上 具体服务器的连接信息会通过接口传入 本来部署是人工来完成的 无非是将一些必须的文件scp到目标服务器上 然后ssh远程登录
  • ubuntu c语言头文件,Ubuntu找不到stdio.h等头文件_安装c库_build-essential安装失败解决...

    最近安装的Ubuntu1804系统 vim gcc都是现安的 用gcc编译时出现找不到头文件情况 于是百度 原来linux类的操作系统上面开发程序 光有了gcc 是不行的 它还需要一个 build essentia 作用是提供编译程序必须软
  • 【无标题】torch.optim.SGD参数详解

    torch optim SGD是PyTorch中实现的Stochastic Gradient Descent SGD 优化器 用于更新神经网络中的参数 以最小化损失函数 从而提高模型的精度 它的一些重要参数如下 lr 学习率 learnin
  • office文档图标显示不正常

    一直用Office2013 前几天用到WPS一个功能 用完后就卸载了 结果电脑中的office文档图标 word excel ppt等 都显示异常 网上查找好久解决了 网址如下 https jingyan baidu com article
  • target_link_libraries 和link_libraries区别

    TARGET LINK LIBRARIES 设置要链接的库文件的名称 语法 TARGET LINK LIBRARIES targetlibrary1
  • 数据结构--并查集

    并查集适用情况 1 有时候 并不关心数据之间的前后关系 也不关心数据的层次关系 一些确定元素只是单纯的聚集在一起 这样的元素聚集集合被称为集合 当希望知道某个数据是否存在一个集合中 或者两个元素是否在同一个集合中时 就需要使用一些集合数据结