并查集(C++)

一、并查集的原理

并查集的两个功能:

  • 合并:合并两个不想联系的元素
  • 查询:判断两个元素是否在同一个组内

主要解决的是元素分组的问题。


例如:某班级要创建三个兴趣小组,分别是象棋、游泳、篮球。
班级总人数10人,现在需要不10人分别添加到三个兴趣小组中,每个人只能参加一个兴趣小组。

象棋小组:{2,4,6,7}
游泳小组:{0,1,5,8}
篮球小组:{3,9}

这里就可以使用到并查集。
在这里插入图片描述
在数组中的表示:

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

在这里插入图片描述


例如:由于篮球小组人比较少,某班级决定解散,成员合并到游泳小组。
在这里插入图片描述

  1. 数组0下标的数据变为-6
  2. 数组3下标的数据变为8

二、并查集的实现

代码实现

1.使用vector容器
2.实现合并功能(一个节点只能附属一个根节点)
3.实现查询功能(判断两个节点的根节点是否相同)
#pragma once
#include<iostream>
#include<vector>
using namespace std;
template<class T>
class UnionFindSet {
public:
	UnionFindSet(const size_t n)
		:UFS(n,-1)
	{}

	//合并查集
	void UnionSet(const int x,const int y)
	{
		int root1 = FindRoot(x);
		int root2 = FindRoot(y);

		if (root1 != root2)
		{
			//合并:控制数量少的往控制数量大的合并(合并意味着层数多了一层)
			if (abs(UFS[root1]) < abs(UFS[root2]))
				swap(root1, root2);

			UFS[root1] += UFS[root2];
			UFS[root2] = root1;
		}
	}
	
	//寻找节点的根
	int FindRoot(int index)
	{
		int temp = index;
		while (UFS[index] >= 0)
		{
			index = UFS[index];
		}

		//子树全部去直接连接根(路径压缩)
		while (UFS[temp] >= 0)
		{
			int next = UFS[temp];
			UFS[temp] = index;
			temp = next;
		}
		return index;
	}	

	//计数森林的个数
	int CountSet()
	{
		int count = 0;
		for (auto n: UFS)
		{
			if (n < 0)
				++count;
		}
		return count;
	}
private:
	vector<T> UFS;
};

路径压缩

1、数量少的森林往数量多的森林合并。(在合并的时候进行优化)
数量多的森林往往代表的查询频率多,如果往数量少的森林上合并,就会使原有的层数+1,提高了查询的成本。
2、子节点全部直接和根节点联系,降低森林的层数。(在查询的时候进行优化)

三、并查集的应用

题1:省份问题
题2:等式方程的可满足性

题1思路:

  • 矩阵的横、纵坐标代表这省份之间的联系,1代表联系需要合并,0代表不联系不需要合并。
  • 最后计算森林的数量

题2思路:

  • 建立字母和数组下标映射关系
  • 合并==的两个字母
  • 最后查询!=的两个字母是否有相同的根,有相同的根则false

结尾

不定期更新,给小编点点赞,为小编加加油,点赞➕收藏不迷路噢~❤️
请添加图片描述

下一篇:讲解图。

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

世_生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值