【高阶数据结构】秘法(一)——并查集:探索如何高效地管理集合
创始人
2024-09-26 00:45:48
0

前言:

前面我们已经学习了简单的数据结构,包括栈与队列、二叉树、红黑树等等,今天我们继续数据结构的学习,但是难度上会逐渐增大,在高阶数据结构中我们要学习的重点是图等

目录

一、并查集的原理

二、并查集的基本操作

三、并查集的实现(简略版)

四、并查集的实现方式和优化策略

五、并查集的实现(完整版)

六、总结


一、并查集的原理

在某些情况下,对于一组元素,我们会把它们划分成不同的集合。起初每个元素组成一个单元素集合,然后按照一定规律将归于同一种类型的集合合并,同时在这个过程中我们可能会反复用到查询某个元素属于哪个集合的运算,这种管理集合所对应的抽象概念就是并查集


并查集,也称为链接-切割数据结构,是一种用于管理集合的高效数据结构。它特别适用于处理“动态连接”的问题,即动态地合并集合或查询两个元素是否属于同一个集合。并查集在计算机科学中有着广泛的应用,如用于解决最小生成树问题(Prim算法和Kruskal算法)、解决网络连通性问题、解决图论中的问题等。


下面来看这样一个例子:某旅游团内有游客10人,其中西安4人,成都3人,武汉3人,10个人来自不同的地方,起先互不相识,每个游客都是一个独立的小团体,现给这些游客进行编号:{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 给以下数组用来存储该小集体,数组中的数字代表:该小集团中具有成员的个数(负数的意义下文讲解)

旅行结束后,游客们要乘车回家,每个地方的游客自发组织成小分队一起上路,于是:西安游客小分队s1={0,6,7,8},成都游客小分队s2={1,4,9},武汉游客小分队s3={2,3,5}就相互认识了,10个人形成了三个小团体。假设右三个群主0,1,2担任队长,负责大家的出行。
一趟火车之旅后,每个小分队成员就互相熟悉,称为了一个朋友圈。
从上图可以看出:编号6,7,8游客属于0号小分队,该小分队中有4人(包含队长0);编号为4和9的同学属于1号小分队,该小分队有3人(包含队长1),编号为3和5的游客属于2号小分队,该小分队有3个人(包含队长1)。 仔细观察数组中内融化,可以得出以下结论:
1. 数组的下标对应集合中元素的编号
2. 数组中如果为负数,负号代表根,数字代表该集合中元素个数
3. 数组中如果为非负数,代表该元素双亲在数组中的下标

回家一段时间后,西安小分队中8号游客与成都小分队1号游客奇迹般的走到了一起,两个小圈子的游客相互介绍,最后成为了一个小圈子: 现在0集合有7个人,2集合有3个人,总共两个朋友圈
通过以上例子可知,并查集一般可以解决一下问题:
1. 查找元素属于哪个集合
沿着数组表示树形关系以上一直找到根(即:树中中元素为负数的位置)
2. 查看两个元素是否属于同一个集合
沿着数组表示的树形关系往上一直找到树的根,如果根相同表明在同一个集合,否则不在
3. 将两个集合归并成一个集合
将两个集合中的元素合并
将一个集合名称改成另一个集合的名称 4. 集合的个数 
遍历数组,数组中元素为负数的个数即为集合的个数。

二、并查集的基本操作

并查集主要支持以下三种基本操作:

  1. 查找(Find):确定一个元素属于哪个集合。
  2. 合并(Union):将两个集合合并为一个集合。
  3. 初始化(Init):为每个元素创建一个独立的集合。

三、并查集的实现(简略版)

根据上面讲的原理和预期功能,我们可以先来实现一个简略版的并查集:
class UnionFindSet { public: 	// 初始时,将数组中元素全部设置为-1 	UnionFindSet(size_t size) 		: _ufs(size, -1) 	{} 	// 给一个元素的编号,找到该元素所在集合的名称 	int FindRoot(int index) 	{ 			// 如果数组中存储的是负数,找到,否则一直继续 			while (_ufs[index] >= 0) 			{ 				index = _ufs[index]; 			}  		return index; 	} 	bool Union(int x1, int x2) 	{ 		int root1 = FindRoot(x1); 		int root2 = FindRoot(x2);  		// x1已经与x2在同一个集合 		if (root1 == root2) 			return false;  		// 将两个集合中元素合并 		_ufs[root1] += _ufs[root2];  		// 将其中一个集合名称改变成另外一个 		_ufs[root2] = root1; 		return true; 	} 	// 数组中负数的个数,即为集合的个数 	size_t Count()const 	{ 		size_t count = 0; 		for (auto e : _ufs) 		{ 			if (e < 0) 				++count; 		}  		return count; 	}  private: 	vector _ufs; };

四、并查集的实现方式和优化策略

并查集有两种常见的实现方式:路径压缩和按秩合并。

  • 路径压缩:在查找操作中,将查找路径上的所有节点的父节点直接指向根节点,以减少查找路径的深度。
  • 按秩合并:在合并操作中,将秩较小的集合合并到秩较大的集合中,以减少树的高度。

为了提高查找操作的效率,通常结合使用路径压缩和按秩合并两种策略。路径压缩确保查找操作的时间复杂度接近常数,而按秩合并则减少了树的高度,进一步优化了合并操作的时间复杂度。

五、并查集的实现(完整版)

#include  #include   class UnionFind { private:     std::vector parent;     std::vector rank;  public:     UnionFind(int n) : parent(n), rank(n) {         for (int i = 0; i < n; ++i) {             parent[i] = i;             rank[i] = 1;         }     }      int find(int x) {         if (parent[x] != x) {             parent[x] = find(parent[x]); // 路径压缩         }         return parent[x];     }      void unite(int x, int y) {         int rootX = find(x);         int rootY = find(y);         if (rootX == rootY) return;          if (rank[rootX] > rank[rootY]) {             parent[rootY] = rootX;         } else if (rank[rootX] < rank[rootY]) {             parent[rootX] = rootY;         } else {             parent[rootY] = rootX;             rank[rootX] += 1;         }     }      bool connected(int x, int y) {         return find(x) == find(y);     } };  int main() {     int n, m;     std::cin >> n >> m;     UnionFind uf(n);     for (int i = 0; i < m; ++i) {         int a, b;         std::cin >> a >> b;         uf.unite(a - 1, b - 1); // 转换为0-based索引     }     for (int i = 0; i < m; ++i) {         int a, b;         std::cin >> a >> b;         std::cout << (uf.connected(a - 1, b - 1) ? "YES" : "NO") << std::endl;     }     return 0; }

六、总结

并查集的高效性在于其优化策略,使得查找和合并操作的时间复杂度保持在较低的水平,从而在处理大规模数据集时依然表现出色。平时我们在刷题或学习中还是会经常遇到并查集的

感谢各位大佬观看,创作不易,还请各位大佬点赞支持!!!

相关内容

热门资讯

安卓系统app更新软件,And... 亲爱的手机控们,你们有没有发现,最近你的手机里那些熟悉的APP们,好像都悄悄地换上了新装呢?没错,安...
手机怎么安双卡安卓系统,轻松实... 你有没有想过,拥有一部可以同时使用两张SIM卡的手机是多么的方便呢?想象一张卡用来工作,另一张卡用来...
安卓系统卸载软件api,功能与... 手机里的软件越来越多,是不是感觉内存都要不够用了?别急,今天就来给你揭秘安卓系统卸载软件的神秘面纱,...
miui操作系统和安卓系统,深... 亲爱的手机控们,今天咱们来聊聊一个让无数米粉心动的系统——MIUI操作系统,还有那个它背后的老大哥—...
原生安卓系统使用教学,原生安卓... 哇,你手里拿的那部手机,是不是也觉得它有点儿特别呢?它可能没有那些花里胡哨的界面,但它却有着自己独特...
安卓系统玩咸鱼之王,三国名将助... 你有没有发现,最近安卓系统上的游戏圈里,有一款叫做《咸鱼之王》的游戏火得一塌糊涂?没错,就是那个让你...
鸿蒙1.0系统是安卓系统吗,揭... 你有没有听说最近华为的鸿蒙1.0系统?是不是有点好奇,这鸿蒙1.0系统是不是安卓系统的“亲戚”呢?别...
优盘安卓系统用桃,U盘安装An... 你有没有想过,你的电脑也能变身成安卓手机?没错,就是那种可以安装各种APP、玩游戏的安卓手机!这可不...
怎样使用安卓8系统,安卓8系统... 你有没有想过,你的安卓手机其实是个小智能助手,只要你会使用,它能帮你做很多事情呢!今天,就让我来带你...
鼎威安卓系统版本,性能升级与用... 你有没有发现,现在车机系统越来越智能了?这不,鼎威的安卓系统版本就让我眼前一亮。想象坐在车里,手指轻...
安卓系统安装抢红包,轻松成为抢... 亲爱的手机控们,是不是每次微信群里抢红包都感觉手慢无?别急,今天我要给你揭秘如何在安卓系统上轻松安装...
写ios系统和安卓系统的人,揭... 你有没有想过,那些默默无闻的程序员们,他们是如何创造出我们每天离不开的iOS系统和安卓系统呢?想象他...
安卓系统设计尺寸规范,适配与优... 亲爱的设计师们,你是否在为安卓系统的设计尺寸规范而头疼?别担心,今天我要带你一起探索这个神秘的领域,...
旧主机改安卓系统,安卓系统改造... 亲爱的读者们,你是否有过这样的经历:家里的旧主机闲置在角落,看着它那略显过时的外观,心里不禁感叹:“...
安卓系统里有趣的,尽在掌握 探索安卓乐园:那些让你笑出声的趣味游戏 开篇:手机里的欢乐小天地想象你手握一部安卓手机,屏幕上跳动...
法兰规格查询系统安卓,安卓版功... 你有没有想过,在繁忙的工程现场,如何快速找到合适的法兰规格呢?别急,今天就来给你揭秘一个神器——法兰...
目前安卓系统最高配置,极致性能... 你有没有发现,现在的手机越来越厉害了,就像是科幻电影里的高科技产品一样。今天,咱们就来聊聊这个话题:...
安卓修改系统返回键,个性化设置... 你有没有发现,手机里的那个小小的返回键,有时候就像是个顽皮的小家伙,让你摸不着头脑?别急,今天就来教...
安卓订餐系统教程视频,从设计到... 你是不是也和我一样,每天忙碌的生活中,最期待的就是那一顿美味的午餐或晚餐呢?现在,有了安卓订餐系统,...
安卓系统限制外部软件,探索外部... 亲爱的手机控们,你是否曾遇到过这样的烦恼:明明打开了“未知来源”,却还是无法安装那些心仪的外部软件?...