数据结构 — 并查集的原理与应用

并查集的原理与应用





并查集是一种树型的数据机构,常用于处理一些不相交集合的合并及查询问题 . 并查集是一种简单的数据结构主要涉及两个基本操作:

1.合并两个不相交的集合.

2.判断两个元素是否属于同一个集合.


并查集的实现原理:


首先并查集会开辟出一个数组,假设数组有10个人,那么就会开辟10个大小的数组,然后每一个下标代表一个人.数组每一个节点的值

都为-1.表示每一 个节点都是相对应的一个集合.大家都不相交.这个时候开始添加关系.如下图:


所以我们发现现在确定一共有几个集合,还有查找两个数据在不在一个集合当中就很容易了. 首先确定几个集合你只需要便利一遍,

如果那个下标的值 小于0,那么就是一个集合.然后判断两个数据是不是在同一个集合里面也很简单. 分别找两个数据的根.如果根

相同那么他两个就在一个集合里,反之 就不在.好了现在思想确定了,那么我们过来实现代码.

class UnionFind
{
public:
	UnionFind(int n)
	{
		_UnionTable.resize(n+1); //因为一般0号下标没有用,所以多开出来一个空间.

		for (int i = 0; i < n+1; ++i)
		{
			_UnionTable[i] = -1;
		}
	}
	
	void AddUnion(int i,int j)
	{
		if (_UnionTable[i] < 0 && _UnionTable[j] < 0)
		{
			_UnionTable[i] += _UnionTable[j];
			_UnionTable[j] = i;
		}
		else
		{
			int k = FindRoot(i);
			int l = FindRoot(j);

			_UnionTable[k] += _UnionTable[l];
			_UnionTable[l] = k;
		}
	}

	int FindRoot(int i)
	{
		assert(i >= 0);

		int k = i;
		while (_UnionTable[k] >= 0)
		{
			k = _UnionTable[k];
		}

		return k;
	}

	int size()
	{
		int count = 0;

		for (int i = 0; i < _UnionTable.size(); ++i)
		{
			if (_UnionTable[i] < 0)
			{
				++count;
			}
		}

		return count - 1;
	}

private:
	vector<int>  _UnionTable;
};

这个代码就是实现一个最简易的并查集,可以用它解决一点实际问题,比如当年小米的一道面试题! 40分啊! 好看题:

假设一组有N个人和M对好友关系(存于数组R).如果两个人是直接或者间接好友(好友的好友就是间接好友),则认为他们属于同一个朋

友圈,请写出程 求出这个N个人里面一共有多少个朋友圈.

例如:N =5,M =3,R = {{1,2},{2,3},{4,5}} 表示有5个人,1和2是还有,2和3是好友,4和5是好友,则1,2,3属于一个朋友圈, 4,5

属于一个朋友 圈. 则一共拥有两个朋友圈.   函数原型 :  int friends(int m,int n,int* r[]);


首先拿到这个题我在不知道并查集这个数据结构之前是真的一头雾水,用其他数据结构真的很难解决.但是使用并查集轻而易举就解决

掉了,不相信?   我下面利用并查集演示解决该题:

class UnionFind
{
public:
	UnionFind(int n)
	{
		_UnionTable.resize(n+1); //因为一般0号下标没有用,所以多开出来一个空间.

		for (int i = 0; i < n+1; ++i)
		{
			_UnionTable[i] = -1;
		}
	}
	
	void AddUnion(int i,int j)
	{
		if (_UnionTable[i] < 0 && _UnionTable[j] < 0)
		{
			_UnionTable[i] += _UnionTable[j];
			_UnionTable[j] = i;
		}
		else
		{
			int k = FindRoot(i);
			int l = FindRoot(j);

			_UnionTable[k] += _UnionTable[l];
			_UnionTable[l] = k;
		}
	}

	int FindRoot(int i)
	{
		assert(i >= 0);

		int k = i;
		while (_UnionTable[k] >= 0)
		{
			k = _UnionTable[k];
		}

		return k;
	}

	int size()
	{
		int count = 0;

		for (int i = 0; i < _UnionTable.size(); ++i)
		{
			if (_UnionTable[i] < 0)
			{
				++count;
			}
		}

		return count - 1;
	}

private:
	vector<int>  _UnionTable;
};

int Friends(int m,int n,int a[][2])
{
	UnionFind T(m);

	for (int i = 0; i < n; i++)
	{
		T.AddUnion(a[i][0],a[i][1]);
	}

	return T.size();
}

void Test()
{
	int n = 5;
	int a[][2] = { { 1, 2 }, { 2, 3 }, { 4, 5 } };

	cout << "一共有"<<Friends(5, 3, a)<<"个朋友圈"<<endl;

}

运行结果为:



  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值