数据结构-并查集

数据结构-并查集

🌟🌟hello,各位读者大大们你们好呀🌟🌟

🚀🚀系列专栏:【Qt的学习】

📝📝本篇内容:并查集概念;并查集的实现

⬆⬆⬆⬆上一篇:经典排序算法(五万字详解,全网最细)

💖💖作者简介:轩情吖,请多多指教(>> •̀֊•́ ) ̖́-

1.并查集概念

在一些应用问题中,需要将n个不同的元素划分成一些不相交的集合。开始时,每个元素自成一个单元素集合,然后按一定的规律将归于同一组元素的集合合并。在此过程中要反复用到查询某一个元素归属于那个集合的运算。适合于描述这类问题的抽象数据类型称为并查集(union-find set),可以把它看做是一个森林,每一个集合是树。

例如刚开学,有好多的新同学,大家一开始都不认识,因此各自一个集合,给每个同学编号,即0-9,数组中的数字代表该小集体中具有的成员个数

接下来随着小组作业要分组,大家就开始认识了,并且选出了组长0,1,2,下面是集合用树型表示的方法

在数组上反应就是像上图一样的情况,并且我们对它进行实现的时候,也是通过vector来实现的。

通过观察上面的数组我们可以发现:

①数组的下标对应集合中元素的编号

②数组中如果为负数,负号代表根结点,数字代表该集合中的元素个数

③数组中如果为非负数,代表该元素的双亲在数组中的下标

当然还是有可能继续合并成一个集合,这这种情况下,原本作为组长的1变成了0的组员,那么它的对应下标存储的内容应该加到0下标的位置上去,使得0成为根结点,0集合中一共有7个结点,而1下标所存储的内容应该变成它的父结点0

2.并查集的实现

cpp 复制代码
#pragma once
#include <vector>
using namespace std;
class UnionFindSet
{
public:
	UnionFindSet(size_t size)//构造函数
		:_ufs(size, -1)//10个位置初始化为-1
	{}

	//找根结点
	size_t FindRoot(int x)
	{
		int parent = x;
		while (_ufs[parent] >= 0)//循环找到根
		{
			parent = _ufs[parent];
		}

		//进行压缩优化
		int root = parent;
		while (_ufs[x] >= 0)//将祖先结点都进行调整为根节点的子结点
		{
			int x_parent = _ufs[x];
			_ufs[x] = root;
			x = x_parent;
		}
		
		return parent;//返回根结点
	}

	//并集
	void Union(int x1, int x2)
	{
		int root1 = FindRoot(x1);
		int root2 = FindRoot(x2);
		if (InSet(x1, x2))//如果已经在一个集合里了
		{
			return;
		}
		else
		{
			//优化,控制数量小的往大的集合合并
			if (abs(_ufs[root1]) < abs(_ufs[root2]))
			{
				swap(root1, root2);
			}


			_ufs[root1] += _ufs[root2];
			_ufs[root2] = root1;

		}
	}

	//有几个集合
	size_t setCount()
	{
		size_t count = 0;
		for (int i = 0; i < _ufs.size(); i++)
		{
			if (_ufs[i] < 0)//<0的都是根
			{
				count++;
			}
		}
		return count;
	}

	//在不在一个集合
	bool InSet(int x1, int x2)
	{
		return FindRoot(x1) == FindRoot(x2);
	}


private:
	vector<int> _ufs;//并查集
};
cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include "UnionFindSet.hpp"
using namespace std;
int main()
{
	UnionFindSet s(10);
	/*cout << s.FindRoot(7) << endl;
	cout << s.FindRoot(9) << endl;
	s.Union(7, 9);
	cout << s.FindRoot(7) << endl;
	cout << s.FindRoot(9);*/

	//根结点没有变化,一直是9--没有优化
	/*s.Union(9, 8);
	s.Union(8, 7);
	s.Union(7, 6);
	s.Union(6, 5);
	s.Union(5, 4);
	s.Union(4, 3);
	s.Union(3, 2);
	s.Union(2, 1);*/


	//根结点不停地发生了变化-根结点为合并本身
	s.Union(8, 9);
	s.Union(7, 8);
	s.Union(6, 7);
	s.Union(5, 6);
	s.Union(4, 5);
	s.Union(3, 4);
	s.Union(2, 3);
	s.Union(1, 2);
	s.FindRoot(9);//没有合并中的优化就需要手动使用FindRoot中的优化来降低深度


	return 0;
}

这里主要是要讲一下它的压缩优化情况,这个地方主要是为了优化"单支树"的情况包括深度太深,使得所有的结点变成根结点的子结点。当我们只要FindRoot这个函数就会进行优化

第二个优化的地方是为了降低深度,如下面第二张图,并且能够在FindRoot中的优化没有起效果的时候(示例中的根结点为合并本身的情况),进行优化"单支树"的情况来降低深度

如图,为的是希望在合并时,是右边合并到左边,这样就不会使得深度更深。

但是并不是说有了第二个优化就可以不需要第一个优化了,当两棵树(两个集合)都不止有一个元素时,想要更好的优化,就需要通过第一个优化方式,进行挨个压缩到与根结点进行相连,因此两个优化是相辅相成的,总得来说优化就是为了降低深度,提高查找根结点的效率。
🌸🌸数据结构-并查集大概就讲到这里啦,博主后续会继续更新更多Qt的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!如有小伙伴需要Qt的安装包可以私信我,你们的支持是博主坚持创作的动力!💪💪

相关推荐
汉克老师23 分钟前
GESP5级C++考试语法知识(六、链表(一)单链表)
c++·链表·单链表·快慢指针·进阶·gesp5级·gesp五级
m0_5180194830 分钟前
C++与机器学习框架
开发语言·c++·算法
wefly201733 分钟前
m3u8live.cn 在线M3U8播放器,免安装高效验流排错
前端·后端·python·音视频·前端开发工具
ZTLJQ33 分钟前
深入理解逻辑回归:从数学原理到实战应用
开发语言·python·机器学习
qq_4176950537 分钟前
C++中的代理模式高级应用
开发语言·c++·算法
学嵌入式的小杨同学1 小时前
STM32 进阶封神之路(十九):ADC 深度解析 —— 从模拟信号到数字转换(底层原理 + 寄存器配置)
c++·stm32·单片机·嵌入式硬件·mcu·架构·硬件架构
xiaoye-duck1 小时前
《算法题讲解指南:动态规划算法--路径问题》--5.不同路径,6.不同路径II
c++·算法·动态规划
ambition202421 小时前
最大子数组和算法全解析:从暴力枚举到动态规划优化
数据结构·c++·算法
波波0071 小时前
每日一题:.NET 中的“反射”是什么?
开发语言·.net
zhanggongzichu1 小时前
小白怎么理解后端分层概念
后端·全栈