高阶数据结构之-并查集

简介: 高阶数据结构之-并查集

并查集原理

并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中

并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题,常常在使用中以森林来表示

适合于描述这类问题的抽象数据类型称为并查集(union-findset)


小例子-编号和人

如果我们想让编号和人构成关系, 即可以通过编号找到对应的人,也可以通过人找到对应的编号怎么做?

template<classT>  

classUnionFindSet

{

public:

   UnionFindSet(constT*a, size_tn)  

   {

       for (inti=0; i<n; i++)

       {

           _a.push_back(a[i]);

           _indexMap[a[i]] =i;

       }

   }

private:

   vector<T>_a;  //编号找人  下标对应的就是人名

   map<T, int>_indexMap;//人找编号  key:人名   value:编号

};

#include"UnionFind.h"

intmain()

{

   stringa[] = { "张三","李四","王五" };

   UnionFindSet<string>ufs(a, 3);

   return0;

}


并查集的实现

#include<iostream>

#include<vector>

classUnionFindSet

{

public:

   UnionFindSet(size_tn);

   size_tFindRoot(intx);

   voidUnion(intx1, intx2);

   boolInset(intx1,intx2);

   size_tSetCount();

private:

   std::vector<int>_set;

};


首先我们要知道如下规则:

  1. 数组的下标对应集合中元素的编号
  2. 数组中的值如果为负数,负号代表根,数字代表该集合中元素个数
  3. 数组中的值如果为非负数,代表该元素双亲在数组中的下标

例如:


基本结构

classUnionFindSet

{

private:

   std::vector<int>_set;

};

构造函数

最初每个元素各自构成一个集合, 初始状态就是-1,表示size个集合

UnionFindSet(intsize)

   :_set(size, -1)//size个元素都最初初始化为-1

   {}


查找根节点

返回根节点所在下标

//如果_set[x]是负数,那么x就是某个集合的根

//如果_set[x]不是负数,那么x的父亲节点就是_set[x]值对应的位置

size_tFindRoot(intx)

{

   while (_set[x] >=0)

   {

       x=_set[x];

   }

   //来到这,_set[x] <0, 此时x就是根节点

   returnx;

}


路径压缩技巧

路径压缩实际上是在数据量太大的时候,访问一些数据可能在位于叶子位置,导致访问的效率不高

本质上我们查找的代表节点的时候,可以对该路径上的节点进行路径压缩

注意:最好不要用递归的时候进行路径压缩,因为路径深可能导致栈溢出


做法:

1)先找到当前路径节点的代表节点

2)然后从当前位置_set[x]开始的节点,沿途的节点的父亲位置都改为代表节点的位置

  只需要根据下标关系往上迭代即可x节点的父亲位置就是_set[x]位置

注意:要提前保存x节点的父亲节点位置,因为更改当前位置的父亲节点, 会丢失以前的父亲节点

size_tFindRoot(intx)

{

   introot=x;

   while (_set[root] >=0)

   {

       root=_set[root];

   }

   //此时root就是x这条路径的头节点(代表节点)

   //把沿途的节点的父亲都改为root

   while (_set[x] >=0)

   {

       intparent=_set[x];//先保存x节点的父节点位置

       _set[x] =root;//将x节点的父亲位置改为root位置

       x=parent;//往上迭代

   }

   returnroot;

}


合并集合


这里选择合并到x2所在的集合合并到x1所在的集合

voidUnion(intx1, intx2)

{

   //找到x1和x2的代表节点(根节点)

   size_troot1=FindRoot(x1);

   size_troot2=FindRoot(x2);

   //两个节点不在一个集合才合并

   //把root2合并到root1所在集合

   if (root1!=root2)

   {

       _set[root1] +=_set[root2];//root2所在集合的元素累加到root1所在集合

       _set[root2] =root1; //root2的父亲变为root1

   }

}

如果我们希望是小集合合并到大集合呢?  即元素少的集合合并到元素多的集合

路径压缩技巧

两颗树合并的时候,节点少的数往节点多的树合并

目的:为了使节点层数增多的节点相对减少


做法:

1)判断哪颗树的节点更多, 让root1变为是较大的集合, root2往root1合并

如何判断呢? 因为root1和root2是根,_set[root]的值是负数,代表该集合的元素个数, _set[root]值越大,集合元素个数越少

//x1和x2合并 ->本质是代表节点(根节点)合并

voidUnion(intx1, intx2)

{

   //找到x1和x2的代表节点(根节点)

   size_troot1=FindRoot(x1);

   size_troot2=FindRoot(x2);

   //我们让root1的集合是大集合,小集合合并到大集合中

   //因为root1和root2是根,所以_set[root]的值为负数,代表集合的元素个数,值越小,元素越多

   if (_set[root1] >_set[root2]) //说明root2集合元素多

   {

       ::swap(root1, root2);

   }

   //两个节点不在一个集合才合并

   if (root1!=root2)

   {

       _set[root1] +=_set[root2];//root2所在集合的元素累加到root1所在集合

       _set[root2] =root1; //root2的父亲变为root1

   }

}


求集合个数

遍历_set,查看有多少元素是负数,就代表有多少个根节点, 即有多少个集合

size_tSetCount()

{

   size_tcount=0;

   for (autoe : _set)

   {

       if (e<0) count++;

   }

   returncount;

}


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

只需要判断两个元素的代表节点的位置是否相同即可

boolInset(intx1,intx2)

{

   returnFindRoot(x1) ==FindRoot(x2);

}



相关文章
|
7月前
|
SQL 供应链 监控
Quick BI使用案例12:如何实现分组内“最新”与“次新”订单时间计算
本文详解订单时效性分析:通过LOD_FIXED与BI_MAX函数,快速计算各区域“最新/次新订单时间”,助力识别交易活跃度、预警客户流失、优化供应链。
|
5月前
|
机器学习/深度学习 存储 缓存
大模型架构算力对比:Decoder-only、Encoder-Decoder、MoE深度解析.71
本文深入解析三大主流大模型架构(Decoder-only、Encoder-Decoder、MoE)的算力消耗差异,聚焦注意力机制复杂度、参数量与计算密度三大维度。通过公式推导、代码模拟与可视化图表,揭示MoE稀疏激活的显著节算优势及瓶颈,剖析长文本场景下的“平方级算力黑洞”成因,并提供面向不同场景的架构选型建议。
1005 20
|
UED
网络性能指标
本内容详细介绍了网络性能中的三个关键指标:时延、抖动和丢包率。时延指数据传输所需时间,影响实时性;抖动表示延迟变化程度,反映网络稳定性;丢包率衡量数据丢失比例,评估传输可靠性。这些指标对在线游戏、视频会议等实时应用至关重要,高时延、大抖动或高丢包率会显著降低用户体验。通过类比快递寄送和语音通话,清晰解释了各指标的定义及应用场景。
3171 8
|
8月前
|
安全 网络安全 调度
直面新型DDoS攻击:基于SDK接入的端到端安全防护架构与技术实现
在数字化浪潮中,游戏、数字藏品、区块链、直播、电商、理财App等已成为互联网经济的核心支柱。然而,这些高价值、高并发的业务也成为了DDoS/CC攻击的重灾区。传统基于流量清洗和IP轮询的防护方案日益乏力,一种基于SDK接入的**端到端加密隧道**与**智能调度**技术正成为防护的新范式。本文将深入剖析其核心原理、架构实现,并结合代码示例,阐述如何为关键业务构建坚不可摧的“数字护盾”。
381 0
|
9月前
|
人工智能 运维 自然语言处理
2025年开源AI知识库深度体验:PandaWiki重新定义企业知识管理
2025年末了,作为一名AI的资深使用者我对PandaWiki有一点使用体会想分享下,写的不好请见谅。
|
存储 数据采集 监控
云上数据安全保护:敏感日志扫描与脱敏实践详解
随着企业对云服务的广泛应用,数据安全成为重要课题。通过对云上数据进行敏感数据扫描和保护,可以有效提升企业或组织的数据安全。本文主要基于阿里云的数据安全中心数据识别功能进行深入实践探索。通过对商品购买日志的模拟,分析了如何使用阿里云的工具对日志数据进行识别、脱敏(3 种模式)处理和基于 StoreView 的查询脱敏方式,从而在保障数据安全的同时满足业务需求。通过这些实践,企业可以有效降低数据泄漏风险,提升数据治理能力和系统安全性。
2413 259
云上数据安全保护:敏感日志扫描与脱敏实践详解
|
人工智能 自然语言处理 程序员
通义灵码 2.5 版发布上线,支持 Qwen3
示例中展示了通义灵码创建贪食蛇游戏的过程,包括代码优化、Bug修复和功能改进(如游戏结束后提示重新开始)。并通过AI总结了工具的核心能力,如实时续写、自然语言生码、单元测试生成等,帮助开发者高效编码并提升代码质量。
578 10
|
存储 缓存 API
信息检索重排序技术深度解析:Cross-Encoders、ColBERT与大语言模型方法的实践对比
本文将深入分析三种主流的重排序技术:Cross-Encoders(交叉编码器)、ColBERT以及基于大语言模型的重排序器,并详细阐述各方案在实际应用中的性能表现、成本考量以及适用场景。
1180 3
信息检索重排序技术深度解析:Cross-Encoders、ColBERT与大语言模型方法的实践对比
|
Arthas Kubernetes Java
字节面试:CPU被打满了,CPU100%,如何处理?
尼恩,一位拥有20多年经验的老架构师,针对近期读者在一线互联网企业面试中遇到的CPU 100%和红包架构等问题,进行了系统化梳理。文章详细解析了CPU 100%的三大类型问题(业务类、并发类、内存类)及其九种常见场景,提供了使用jstack和arthas两大工具定位问题的具体步骤,并分享了解决死锁问题的实战案例。尼恩还强调了面试时应先考虑回滚版本,再使用工具定位问题的重要性。此外,尼恩提供了丰富的技术资料,如《尼恩Java面试宝典》等,帮助读者提升技术水平,轻松应对面试挑战。
字节面试:CPU被打满了,CPU100%,如何处理?
|
机器学习/深度学习 人工智能 编解码
AI生成壁纸的工作原理
AI生成壁纸基于深度学习和生成对抗网络(GANs),通过生成器与判别器的对抗学习,以及条件生成对抗网络(CGANs)来创造特定风格的壁纸。技术还包括风格迁移、深度卷积生成对抗网络(DCGAN)、潜在空间扩展和自注意力机制。审美评价机制的引入确保了生成的壁纸既符合技术标准又有艺术价值。CGANs能根据用户条件生成个性化壁纸,而风格迁移技术通过多种方法实现图像风格转换。DCGAN和其他GAN变体在处理图像数据时有优势,如高质量样本生成和特征学习,但也存在图像质量、训练效率和模式崩溃等问题。通过构建审美评估模型和使用XAI技术,AI在生成壁纸时能更好地平衡技术与艺术标准。