【数据结构入门精讲 | 第十六篇】并查集知识点及考研408、企业面试练习

本文介绍了并查集的概念、基本操作和优化策略,并提供了相关习题,适用于数据结构学习、考研408备考及面试准备。并查集是一种处理元素分组的数据结构,常用于解决连通性问题,如图论中的连通性判断、最小生成树等。文章包含选择题、填空题和编程题,帮助读者巩固理解。
摘要由CSDN通过智能技术生成

上一篇中我们进行了散列表的相关练习,在这一篇中我们要学习的是并查集。

在这里插入图片描述

在许多实际应用场景中,我们需要对元素进行分组,并且在这些分组中进行查询和修改操作。比如,在图论中,我们需要将节点按照连通性进行分组,以便进行最小生成树、最短路径等算法;在计算机视觉中,我们需要将像素进行分组,以便进行图像分割和对象识别等任务。而并查集正是为了解决这些问题而被提出来的一种数据结构。

概念

并查集(Disjoint Set)是一种用于处理元素分组的数据结构,通常用于解决一些与等价关系有关的问题,比如连通性的判断、最小生成树算法中的边的合并等。

在这里插入图片描述

并查集中的每个元素都属于一个集合,每个集合都有一个代表元素(也称为根节点),代表元素可以用来表示整个集合。并查集支持三个基本操作:

1.MakeSet(x):创建一个只包含元素 x 的新集合;
2.Find(x):返回元素 x 所属的集合的代表元素;
3.Union(x, y):将元素 x 和 y 所属的集合合并成一个新集合。

其中,Find 操作可以使用路径压缩(Path Compression)和按秩合并(Union by Rank)优化,以提高查询效率。

并查集的应用非常广泛,比如在图论算法中求解连通性、求解最小生成树等问题时都会用到。

伪代码

// 初始化并查集,每个元素单独成集合
function MakeSet(x)
    x.parent = x
    x.rank = 0

// 查找元素所属的集合(根节点),并进行路径压缩
function Find(x)
    if x.parent != x
        x.parent = Find(x.parent) // 路径压缩:将x的父节点设为根节点
    return x.parent

// 合并两个集合,按秩合并
function Union(x, y)
    xRoot = Find(x)
    yRoot = Find(y)
    if xRoot == yRoot
        return // 已经在同一个集合中,无需合并
    if xRoot.rank < yRoot.rank
        xRoot.parent = yRoot
    else if xRoot.rank > yRoot.rank
        yRoot.parent = xRoot
    else
        yRoot.parent = xRoot
        xRoot.rank = xRoot.rank + 1

接下来,让我们进行并查集的相关练习。

选择题

1.在这里插入图片描述

选B

2.在这里插入图片描述

解析:
1  -4   1  1  -3  4  4  8  -2
0   1   2  3   4  5  6  7   8


1对应-4,则1是根节点且有4个子孙
又因为023都对应1
所以
      1
   0 2 3 null
   
   
4对应-3,则4是根节点且有3个子孙
又因为56都对应4
所以
    4
  5  6 null
  
  
8对应-2,则8是根节点且有2个子孙
又因为7对应8
所以
    8
  7 null
  
将68所在的集合合并,且小集合合并到大集合
则
    4
  5  6 8
      7 null
所以树根是4,对应的编号是-5-表示树根,5表示4的子孙个数)

3.在这里插入图片描述

可以画出来对应的树
然后把小树连到大树上
接着从17遍历
如果有父节点,给出父节点的值
如果它本身是根节点,则给出负号和子孙个数

在这里插入图片描述

填空题

在这里插入图片描述

在这里插入图片描述

编程题

7-1 朋友圈

某学校有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
#include<stdio.h> 

int a[30001];  // 定义数组a,用于存储并查集的父节点信息
int search(int b){
     // 查找元素所属的集合(根节点)
	if(a[b]<0){
     // 如果a[b]小于0,说明b是根节点
		return b;  // 返回b作为集合的代表元素
	}else{
   
		return search(a[b]);  // 否则递归查找父节点,直到找到根节点
	}
}

void function(int m,int n){
     // 合并两个集合
	int x,y;
	x=search(m);  // 查找m所属的集合(根节点)
	y=search(n);  // 查找n所属的集合(根节点)
	if(x!=y){
     // 如果m和n不在同一个集合中
		a[x]+=a[y];  // 将集合y的大小加到集合x上
		a[y]=x;  // 将集合y的父节点指向集合x
	}
}

int main(){
   
	int m,n;
	scanf("%d %d",&n,&m);  // 输入学生数量n和关系数量m
	int i;
	for(i=0;i<=n;i++){
     // 初始化并查集,每个元素单独成集合
		a[i]=-1;  // 初始时每个元素的父节点为自身,且集合大小为1
	}
	int stu,j,num,num1;
	for(i=0;i<m;i++){
     // 处理每组关系
		scanf("%d",&stu);  // 输入每组关系中学生的数量
		for(j=0;j<stu;j++){
     // 输入每组关系中的学生编号
			scanf("%d",&num);
			if(j==0){
   
				num1=num;  // 记录第一个学生的编号
			}else{
   
				function(num1,num);  // 合并这组关系中的学生
			}
		}
	}
	int min;
	min=a[1];
	for(i=2;i<n;i++){<
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

秋说

感谢打赏,祝你平安喜乐。

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

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

打赏作者

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

抵扣说明:

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

余额充值