并查集实现解决小米面试题朋友圈问题

并查集:将N个不同的元素分成一组不相交的集合,开始时,每个元素就是一个集合,然后按规律将两个集合进行合并。

举例如:(1)现在有10个元素:0,1,2,3,4,5,6,7,8,9;分别将每个元素看成一个集合,则可将它们看作数组下标,数组里先存储-1,代表都为根,如:


(2)现在知道有元素下列不相交集合关系:

则在上面数组基础上创建这些集合关系,数组变为:


过程为:以第一个集合为例,6,7,8的根都为0;则在0的位置加上数组位置6,7,8的值-1,此时0位置变为-4,再将6,7,8位置值存储它们的根节点(当然它们是一个一个元素进行实现的),0根节点此时存储的值的绝对值则为这个集合元素的个数。剩下的集合思想相同。

3.此时已经创建好以上集合关系,若现在将以上集合关系改为相交,如下:

即把第二个集合连接在第一个集合上,0变为两个集合共同根节点,此时数组从2基础上变为:


过程则为:先找到两个集合的根节点,第1个集合根节点0位置值+=第2个集合根节点位置值,再将第二个集合位置值变为第一个根节点0,此时则将两个集合连接了起来。

这就为并查集的实现过程,可用其解决一道面试题朋友圈问题,题目如下:

假如已知有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属于另一个朋友圈,结果为2个朋友圈。

代码实现:

#include <iostream>
using namespace std;
//并查集解决朋友圈问题
class UnionSet
{
public:
	UnionSet(size_t n)
		:_n(n)
	{
		_parent=new int[n+1];
		memset(_parent,-1,sizeof(int)*(n+1));//将数组初始化为-1
	}
	void Union(int r1,int r2)
	{
		int root1=Find(r1);
		int root2=Find(r2);
		if(root1!=root2)
		{
			_parent[root1]+=_parent[root2];
		    _parent[root2]=root1;
		}
	}
	size_t Find(int index) //查找某一个朋友圈根节点
	{
		size_t root=index;
		while(_parent[root]>0)
		{
			root=_parent[root];
		}
		return root;
	}
	size_t CountRoot()//统计根节点
	{
		size_t count=0;
		for(size_t i=1;i<=_n;++i)
		{
			if(_parent[i]<0)
			{
				++count;
			}
		}
		return count;
	}
protected:
	int* _parent;  
	size_t _n;   //人的个数
};
size_t Friends(int arr[][2],int n,int m)
{
	UnionSet un(n);
	for(size_t i=0;i<m;++i)
	{
		un.Union(arr[i][0],arr[i][1]);
	}
	return un.CountRoot();
}
void TestUnionSet()
{
	//int arr[][2]={{1,2},{2,3},{4,5}};
	//int n=5;
	//int m=3;
	int arr[][2]={{1,2},{2,4},{3,5},{5,6},{4,7},{9,8},{8,9}};
	int n=9;
	int m=7;
	size_t ret=Friends(arr,n,m);
	cout<<"朋友圈个数为:"<<ret<<endl;
}
#include "UnionSet.h"
#include <cstdlib>
int main()
{
	TestUnionSet();
	system("pause");
	return 0;
}



  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值