算法铺子之并查集(一)

本人已经打算将这一系列博文做成动画趣味科普的形式来呈现,感兴趣的话可以点这里

0.并查集是啥?

并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。
集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并。

1.并查集能干啥?

并查集这种数据结构主要用来解决连接问题,什么叫连接问题呢?下面举个栗子,有一个图,里面有很多点,这些点可能和其他点是相连的。如下图所示,那么如果我想知道左上角的点和右下角的点是不是相连通的,你会怎么办?

这里写图片描述

把图读进内存然后深搜么?虽然可是可以,但深搜解决的是路径问题(即找出起点到终点的详细的路径),我们只需要知道是否相连,可想而知深搜的时间复杂度太高,而且数据量大的时候空间也扛不住。此时并查集就可以派上用场了。

2.实现一个基本的并查集

一个并查集对一组数据应该起码支持两种操作,一个是union(p,q)(也就是将p和q连接起来),另一个是find§(也就是查一下p属于哪个集合)。当然,有了这两种操作之后,就能轻松解决两点是否连接的问题(isConnected(p,q))。

2.1 并查集基本的数据表示

比如我现在有一组10个元素的数据,我们不妨以一个数组来表示。

这里写图片描述

图中红线上方代表的是数据的索引,红线下方表示的是元素所属集合的编号。也就是说0-4号元素是相连接的(属于同一个集合,集合编号为0),5-9号元素是相连接的(属于同一个集合,集合编号为1)。

2.2 find§

从2.1的数据表示来看,想要得到当前元素所属的集合编号,只要返回元素的值即可。

	//查找p所属的集合编号
	int find(int p)
	{
		assert(p >= 0 && p < count);
		return id[p];
	}

2.3 isConnected(p,q)

有了find操作后,判断两个元素是否属于同一个集合也很容易。

	//判断p和q是否属于同一个集合
	bool isConnected(int p, int q)
	{
		return find(p) == find(q);
	}

2.4 union(p,q)

union操作是将两个元素所代表的集合合并到同一个集合当中,这个操作其实只要将q所在的集合中所有元素的集合编号换成p的集合编号即可。

	//将两个元素的集合并在一起
	void unionElements(int p, int q)
	{
		int pID = find(p);
		int qID = find(q);

		//p和q本来就是一个集合的,不做处理
		if (pID == qID)
		{
			return;
		}

		for (int i = 0; i < count; ++i)
		{
			if (id[i] == pID)
			{
				id[i] = qID;
			}
		}
	}

2.5 UnionFind代码

class UnionFind
{
public:
	UnionFind(int n)
	{
		count = n;
		id = new int[count];

		//初始的时候每个元素属于自己独立的集合
		for (int i = 0; i < count; ++i)
		{
			id[i] = i;
		}
	}
	~UnionFind()
	{
		delete [] id;
	}

	//查找p所属的集合编号
	int find(int p)
	{
		assert(p >= 0 && p < count);
		return id[p];
	}

	//判断p和q是否属于同一个集合
	bool isConnected(int p, int q)
	{
		return find(p) == find(q);
	}

	//将两个元素的集合并在一起
	void unionElements(int p, int q)
	{
		int pID = find(p);
		int qID = find(q);

		//p和q本来就是一个集合的,不做处理
		if (pID == qID)
		{
			return;
		}

		for (int i = 0; i < count; ++i)
		{
			if (id[i] == pID)
			{
				id[i] = qID;
			}
		}
	}

private:
	int *id;			//集合编号数组
	int count;			//集合编号数组的大小
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

alw_123

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

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

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

打赏作者

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

抵扣说明:

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

余额充值