数据结构与算法——并查集

本文通过一个具体的实例介绍了如何使用并查集解决寻找朋友圈中最大朋友圈人数的问题。并查集是一种用于处理集合合并与查询的抽象数据类型,通过查找每个元素的根节点来判断元素间是否属于同一集合。文章详细解释了并查集的基本操作,包括find()和union()函数,并给出了C++代码实现。最后,文章展示了如何应用这些概念解决朋友圈问题。
摘要由CSDN通过智能技术生成

朋友圈 

某学校有N个学生,形成M个俱乐部。每个俱乐部里的学生有着一定相似的兴趣爱好,形成一个朋友圈。一个学生可以同时属于若干个不同的俱乐部。根据“我的朋友的朋友也是我的朋友”这个推论可以得出,如果A和B是朋友,且B和C是朋友,则A和C也是朋友。请编写程序计算最大朋友圈中有多少人。

输入格式:
输入的第一行包含两个正整数N(≤30000)和M(≤1000),分别代表学校的学生总数和俱乐部的个数。后面的M行每行按以下格式给出1个俱乐部的信息,其中学生从1~N编号:

第i个俱乐部的人数Mi(空格)学生1(空格)学生2 … 学生Mi

输出格式:
输出给出一个整数,表示在最大朋友圈中有多少人。

输入样例:
7 4
3 1 2 3
2 1 4
3 5 6 7
1 6
输出样例:
4

这题是经典的并查集题目

那么先讲解何为并查集

等级划分

 这边引用别的博主的一张图片

假设分为明教,少林寺,峨嵋派,西毒四个帮派,在帮派战争中,队友如何识别队友和敌人呢,

比如金毛狮王和紫衫龙王如何知道自己是队友呢,答案就是他们有个教主叫做张无忌,其他人

也是一样,例如小昭和胡青牛,他们有共同的上级紫衫龙王,紫衫龙王有上级张无忌,殷野王上级有白眉鹰王,白眉鹰王上级有张无忌,所以他们全部都隶属于张无忌的部下,那么他们就知道对方是不是自己的队友。

并查集也是如此,我们要将一群一群的数据分类,如何知道这个数据是这个类呢,只要设置一个根

所有的祖先节点都为这个根,就知道他们都是同一个类,否则不是。

因此并查集就要有两个函数 find(int target)  和 union(int x,int y)

find函数用来查找目标数的祖先数,union用来将一个数并入另一个数的类中。

find如何操作呢:首先设置pre[]数组 初始化时还没分类 每个人单独为一个类 那么pre[ i ] = i;

当union将这个数并入一个类的时候,i 的祖先结点就不是它本身了,而是另一个j 属的类的祖先结点 也就是  将j的祖先结点赋值为i的祖先结点   pre[ i ] = pre[ j ]  这就是union的操作

那么find查找的祖先结点的时候只要 return  pre[ i ]就可以了

下面附上上面那题朋友圈的代码 帮助理解

#include<bits/stdc++.h>
using namespace std;
const int  N=30005;					//指定并查集所能包含元素的个数(由题意决定)
int pre[N];     					//存储每个结点的前驱结点 
int rank1[N];    					//树的高度 

 
int find(int x)     				//改进查找算法:完成路径压缩,将 x的上级直接变为根结点,那么树的高度就会大大降低 
{
    if(pre[x] == x) return x;		//递归出口:x的上级为 x本身,即 x为根结点 
    pre[x] = find(pre[x]);
    return pre[x];  //此代码相当于先找到根结点 rootx,然后 pre[x]=rootx 
} 

bool isSame(int x, int y)      		//判断两个结点是否连通 
{
    return find(x) == find(y);  	//判断两个结点的根结点(即代表元)是否相同 
}

bool join(int x,int y)
{
    x = find(x);						//寻找 x的代表元
    y = find(y);						//寻找 y的代表元
    if(x == y) return false;			//如果 x和 y的代表元一致,说明他们共属同一集合,则不需要合并,返回 false,表示合并失败;否则,执行下面的逻辑
    if(rank1[x] > rank1[y]) pre[y]=x;		//如果 x的高度大于 y,则令 y的上级为 x
    else								//否则
    {
        if(rank1[x]==rank1[y]) rank1[y]++;	//如果 x的高度和 y的高度相同,则令 y的高度加1
        pre[x]=y;						//让 x的上级为 y
	}
	return true;						//返回 true,表示合并成功
}
int main(){
	
	int n,m;
	cin>>n>>m;
	for(int i = 0; i < n; i++){
        pre[i] = i;     			//每个结点的上级都是自己 
        rank1[i] = 1;    			//每个结点构成的树的高度为 1 
    } 
	int num;
	int pre,temp;
	for(int i=0;i<m;i++)
	{
		cin>>num;
		for(int j =0;j<num;j++)
		{
			
			cin>>temp;
			
			if(j==0)
			{
				pre = temp;
				continue;
			}
			if(find(pre) != find(temp))
			join(temp,pre);
			
		}
	}
	map<int,int>mp;
	int msxx=0;
	for(int i = 1;i<=n;i++)
	{
		mp[find(i)]++;
		if(mp[find(i)]>msxx)
		{
			msxx = mp[find(i)];
			}
	}
	cout<<msxx;
	
	
	
	
	
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值