<数据结构>并查集

目录

并查集概念

合并

 查找集合的数量

并查集类代码实现


并查集概念

并查集和堆一样,都是通过数组来实现树的节点映射,不过并查集作用是,把一堆数据分为不同的几个小集合

不过并查集是森林的概念,并查集的学习可以帮助我们去更好的学习图这个数据结构

这里我们举一个例子:

假设我们有一个旅游团,一共有十个人,编号是0~9;

其中 s1 = {0,6,7,8} 这几个人来自湖北

        s2 = {1,4,9} 这几个人来自四川

        s3 = {2,3,4} 这几个人来自北京

上面我们把十个人分为了不同几个小组,他们来自不同的地方,吃的口味自然不同,于是就分别让0,1,2这三个人担任组长,带领各自的队伍去吃饭。

队长可以抽象的理解为头结点,组员理解为各自的孩子结点

<数据结构>并查集_第1张图片

数组的初始化规则定义如下:

1.每一个下标存放自己的双亲结点下标

2.如果该结点是双亲结点,存放一个负数,我们可以顺便存放这棵树的结点个数

特点:

  • 一个位置是负数,那他就是树的根,这个绝对值就是棵树的结点个数
  • 一个位置值是负数那他就是双亲的下标

<数据结构>并查集_第2张图片

合并

我们假设上面的s1和s2发生了合并,咋办呢?

步骤:

  • 先找到两个需要合并集合的双亲结点
  • 再把集合2的根下标存放的值+=集合1的根下标,在把集合2的根下标存放的值置为集合1的根

<数据结构>并查集_第3张图片

 查找集合的数量

我们只需要将整个数组遍历一遍,找到为负数的位置有多少个,即集合的数量

并查集类代码实现

class UnionFindSet
{
public:
	UnionFindSet(size_t n)
		:_ufs(n, -1)
	{}
	
	void Union(int x1, int x2)
	{
		// 1.先找根
		int root1 = FindRoot(x1);
		int root2 = FindRoot(x2);
		//如果在一个集合就没有必要去合并
		if (root1 == root2)
		{
			return;
		}
		//将集合2合并到集合1
		_ufs[root1] += _ufs[root2];
		_ufs[root2] = root1;
	}

	// 找根/双亲
	int FindRoot(int x)
	{ 
		int parent = x;
		while (_ufs[parent] >= 0)
		{
			parent = _ufs[parent];
		}

		return parent;
	}

	bool InSet(int x1, int x2)
	{
		return FindRoot(x1) == FindRoot(x2);
	}

	size_t SetSize()
	{
		size_t size = 0;
		for (size_t i = 0; i < _ufs.size(); ++i)
		{
			if (_ufs[i] < 0)
			{
				++size;
			}
		}

		return size;
	}

private:
	vector _ufs;
};

你可能感兴趣的:(数据结构,数据结构,算法)