07.并查集(笔记)

文章目录

1.并查集的定义

并查集是一个多棵树的集合(森林)。

并查集由多个集合构成,每一个集合就是一颗树。

并:合并多个集合。查:判断两个值是否在一个集合中。

  • 存储结构:数组
  • 初始化:数组元素全部初始化为-1
  • 存储数据:根节点为负数,其绝对值为集合中元素的个数,孩子结点中存放父节点的下标。

eg:

一共10个人

0 1 2 3 4 5 6 7 8 9

开始时这10个人各为一个集合,没有父节点,这里将数组全部设置为-1。

在这里插入图片描述

之后,将10个人分成3组,每组以树的形式存储,树的根节点可以任意取。

这里假设0 4 5 一组,1 3 6 一组,2 7 8 9 一组。

在这里插入图片描述

使用双亲表示法:根节点没合并一个,对应数组的值减1,最终合并结束后,对应的绝对值就是节点的个数。

在这里插入图片描述

之后假设一组和二组合并,只需要更新数组的对应的值即可。

在这里插入图片描述

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class UnionFindSet
{
public:
	// 默认全部都无父节点,为-1
	UnionFindSet(size_t sz)
		:_ufs(sz, -1)
	{}
	
	// 合并两个集合
	bool Union(int x, int y)
	{
		int xroot = FindRoot(x);
		int yroot = FindRoot(y);
		// x,y为同一个集合的话合并失败
		if (xroot == yroot)
			return false;

		// 优化:数据量小的结点并入数据量大的结点
        // 代码中的优化可要可不要,对于效率影响不大。
		if (abs(_ufs[xroot]) < abs(_ufs[yroot]))
			swap(xroot, yroot);
		// 默认并入xroot,要保证xroot的数据量大
		_ufs[xroot] += _ufs[yroot];
		_ufs[yroot] = xroot;
		return true;
	}

	// 核心代码:查找一个子集的根
	int FindRoot(int x)
	{
		int root = x;
		// 查找到结点权值为负的根
		while (_ufs[root] >= 0)
		{
			root = _ufs[root];
		}
		// 如果树的层数太高,我们就无法保证O(1)的时间复杂度
		// 优化:将被查找结点及以上的结点直接连接到根
		int cur = x;
		while (_ufs[cur] >= 0)
		{
			int parent = _ufs[cur];
			_ufs[cur] = root;
			cur = parent;
		}
		return root;
        
        //重复迭代的代码太长,我们也可以使用递归实现,两行代码完成查找+路径优化:
        //if (_ufs[x] >= 0) _ufs[x] = FindRoot(_ufs[x]);
        //return _ufs[x] < 0? x : _ufs[x];
	}

	// 获取有多少个集合
	int SetCount()
	{
		int cnt = 0;
		for (int e : _ufs)
		{
			if (e < 0)
				cnt++;
		}
		return cnt;
	}

	// 查询两个元素是否在同一个集合中
	bool IsInSet(int x, int y)
	{
		return FindRoot(x) == FindRoot(y);
	}
private:
	vector<int> _ufs; // 根节点为负数,其绝对值为集合中元素的个数,孩子结点中存放父节点的序号
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值