当前位置: 首页 > news >正文

5.5.2_1并查集

知识总览:

并查集:

并查集就是一种集合,合并+查找====》集合

集合中元素只有2种关系,属于这个集合或者属于另外一个集合(或者说不属于这个集合)

一个集合里有好多元素,把同类型的元素构造成一棵树,其他类元素构造成另外一棵树,这个元素属于哪个集合就用这棵树的根节点表示,讨论哪个元素属于哪个集合,就是找这个元素所在树的根节点。森林是由多个互不相交的树构成的

用互不相交的树来表示集合的用途:

1.判断某个元素属于哪个集合------》从该元素出发,一路向北查到该元素所在根节点,根节点是哪个就是哪个树,哪个集合

2.判断2个元素是否从属相同集合---》各个元素一路向北查到各元素所在根节点,就是各元素所在哪个树,哪个集合,再判断2个根节点是否相等,相等即为在同一个集合,不相等即为在不同集合

3.合并子集操作。即把2个集合合并成一个集合,可以把一个子集放到另外一个子集里成为另外一个子集的子集。即把一棵树放到另外一棵树里成为另外一棵树的孩子。如例子喜欢紫葡萄、绿葡萄的人各自为一个子集,则可把2个子集合并成一个都喜欢葡萄的集合,即可让喜欢紫葡萄的树归属到喜欢绿葡萄树的子树即可

 

为什么用树的双亲表示法表示并查集:用孩子节点字段中的parent指针指向该节点的父节点所在的数组下标,查询父节点快,查找孩子节点慢(因为要遍历所有的节点,找parent是该节点的),而并查集的查是一路向北找父节点的,所以用双亲表示法更容易找父节点,并且并查集的并是把一棵树A合并到另外一棵树B下成为树B的子树,如果用双亲表示法直接把A的根节点的parent字段改成B的根节点就可完成合并,综上,用双亲表示法表示并查集很方便。

并查集的存储结构:

就是长度为n的int型数组S。和树的双亲表示法类似,即假如所有元素个数为n,则定义一个长度为n的int型数组S,把元素依次放在数组中,每个元素对应一个下标,数组中的值即为父节点的数组下标,即实际数组存储的是该元素父节点的索引下标,注意的是该数组S存储的是所有元素的父节点的数组下标,即使有的节点元素不属于同一集合,没有父节点的数组值为-1,如最后一张图,三个树三个集合统一放到S数组里,根节点ACD在数组中的值为-1,如果要把C合并到A,则直接修改C对应的数组值为为A节点的下标即可,即从-1修改为0,就完成了并查集的合并,并查集的查找即查找父节点直接查找S中的值可根据下标直接定位到父节点元素

 

代码实现:

并查集的初始化操作:初始化S[]数组中的值都为-1。并查集就是用一个Int型的数组进行表示即UFSets,开始并不知道这些元素是否属于一个集合,所以把每个元素都初始化为一个个单独的子集,即一个元素一个集合,完成这个操作只需要把数组上的每个元素的值设为-1即可,即表示每个元素都是单独的子集。然后开始进行并查操作,应该属于同一个集合的元素就合并成一个集合。

并查集的查Find操作:

即找某个元素的所在树的根节点。Find中x指的是元素所在数组中的下标(注意不是S[]数组中的值,S[]数组中的值是元素下标的父节点下标值),目前看数组S好像就是存的是元素的父节点数组下标,数组中没其他字段内容。如要查L元素即下标为11即x=11的属于哪个集合,一路向北查到根节点,S[11]=4>=0,即下一轮while循环,X=4,S[4]=1>= 0,下一轮while循环,X=1,S[1]=0>=0,下一轮while循环,X=0,S[0]=-1<=0,退出while循环,返回X=0,依次为查找L的父节点为E,E的父节点B,B的父节点A,A没有父节点,结束。即L属于A节点所在集合。

并查集的并Union操作:

即将2个不想交的集合合并成一个,修改其中一个集合中的父节点为另外一个节点的下标。要传入2个集合(2个树)的根节点下标,如合并AC集合,要传入A数组下标0,C数组下标2,即Root1=0,Root2=2,即把Root2合并到Root1即合并C到A,先判断Root1==Root2直接返回即2个相同集合的不用合并,再把S[Root2]=Root1,即S[2]=0,即把要变成子树的根节点S[]中的值变成另外一个树的根节点数组下标,即修改Root2的父节点数组索引下标由-1改为0

如果在合并的时候给的Root1和Root2节点并不是集合的根节点,那么先通过Find操作找到这俩节点的根节点,再进行Union操作

时间复杂度分析:

union操作只需修改数组中的值,时间复杂度为O(1)

Find操作要根据树的形态确定时间复杂度,如果是最坏单支情况可能需要进行n次while循环即O(n),好的情况可能只需找一层或不用找吧即O(1),即让树的高度h变低有助于减少时间复杂度

union操作的优化

让树的时间复杂度变小,即让树的高度h变小,则在合并时让小树合大树。因为小树合大树只是让小树的父节点变成大树的,大树的高度h不会改变(大树一般会比小树高,合到大树之后,小树只是作为一个子树出现,一般应该不会影响大树的高度),如果让大树合到小树上,大树的高度h一般>小树高度h,合过去之后大树作为子树出现,则起码大树高度h还高一层,且大树节点数>小树节点数,则大树节点在进行Find操作时都要再至少加一层高度

具体优化:

如果是各个集合的根节点,优化前根节点在S[]中的值为-1,优化后让S[]中的值<0,但是S[]代表的值为整个树的节点个数,比如A集合即A树有6个节点,A的index=0,优化前S[0]=-1,优化后S[0]=-6,同理C集合,S[2]=-1变成S[2]=-2,小树合大树即C合到A(因为C有2个节点,A有6个节点,A是大树),合并之后,A中多了2个节点,即A共有8个节点,S[0]=-8,C不再是根节点,变成S[2]=S[0]

优化代码实现如下:

1.用根节点的绝对值表示树的节点总数2.union操作,小树合并大树

过程:目前认为传入的2个节点都是根节点,如果不是先去做find操作。判断Root1和Root2是否相等即是否是相同集合,相同集合不做操作直接return。如果S[Root2]>S[Root1]则Root2合并到Root1(2个S值都为负数的情况下,证明root1的节点数更多),Root1是大树Root2是小树,让S[Root1]+=S[Root2],S[Root2]=Root1,即Root1总节点数增加,Root2父节点改变成Root1,相反同上个过程

优化之后结果:Find操作时间复杂度变成O(log2n),union操作时间复杂度不变还是O(1),合并后构造的树的高度不超过log2n向下取整+1

知识回顾:

写得好罗里吧嗦,其实知识点并不复杂。。。。。。。。。。。。 

 

相关文章:

  • Vue3优质动画库推荐
  • 在windows10上安装nvm以及配置环境
  • Claude Code 是什么?
  • 刷leetcode hot100返航版--字符串6/15
  • python动态蓝色蝴蝶爱心
  • 目标分割数据集大全「包含分割数据标注+训练脚本」 (持续原地更新)
  • 设计模式(10)——创建型模式之抽象工厂
  • Python 文件操作详解
  • 电脑上的.ssh目录只做什么的
  • AMD Pensando Pollara 400Gbps网卡深度解析:超级以太网重塑AI集群网络架构
  • linux多线程之POSIX信号量
  • Python变量与数据类型全解析
  • AI视野:视频处理AI排行榜Top10 | 2025年05月
  • SpringJPA统计数据库表行数及更新频率
  • 37-Oracle 23 ai Shrink Tablespace(一键收缩表空间)
  • 打卡day54
  • Pico rp2040开发之Vscode插件+ c/c++独立环境搭建
  • Python中async协程快速理解
  • Vue3相关知识1
  • SHA-2
  • 网站做信用认证有必要吗/百度统计怎么用
  • 专业商城网站建设价格低/企业网站制作多少钱
  • 到那里找做网站的兼职/百度自己的宣传广告
  • 利用免费网站做SEO/域名收录
  • 模板网站库/2022千锋教育培训收费一览表
  • 上海优化网站/技能培训班有哪些课程