基础数据结构第八期 并查集

2024-01-08 06:40:16

前言

并查集这部分还是挺重要的,应该要熟练掌握哦!!!

一、并查集的基本概念

作用:

1、将两个集合合并

?2、查询是否在一个集合内

?基本原理:

每个集合用一棵树来表示,树根的编号就是整个集合的编号,每个节点储存它的父节点,p[x]表示x的父节点。

问题:

1、如何判断树根:if(p[x] == x)

2、如何求x的集合编号:while(p[x] != x) x = p[x]

3、如何合并两个集合:px是x的集合编号,py是y的集合编号,p[x] = y

二、例题

1.合并集合:

#include<iostream>
using namespace std;

const int N = 1e5+10;
// p数组是做什么的?---存储每个节点的根节点
// p[x] != x代表它是一个根节点
int p[N],a,b;

// 递归---先递归到最底层,再开始回溯赋值
// find函数的作用:找到x的根节点并返回,并压缩路径
int find(int x){
    if(p[x] != x) p[x] = find(p[x]); // 压缩路径,将查询x的所有长辈节点直接接在根节点上
    return p[x];
}

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i < n;i++) p[i] = i;
    while(m--){
        char op[2];
        scanf("%s%d%d",op,&a,&b);
        // 合并---让a集合直接连接到b集合的根节点下
        if(*op == 'M') p[find(a)] = p[find(b)];
        else{
            // 查询是否是同一个集合,如果a集合和b集合具有相同的根节点,则它们是同一个集合
            if(find(a) == find(b)) puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

2.连通块中点的数量:


//P23连通块中点的数量
//并查集
#include <iostream>
using namespace std;
const int N = 100010;
int n, m;
//p存储每个点
//size表示每一个集合的大小,每一个集合里元素的个数,就是存储根节点所拥有的子节点数
//只保证根节点的size有意义
int p[N], sizee[N];

int find(int x) {//返回祖宗节点+路径压缩
	if (p[x] != x)
		p[x] = find(p[x]);
	return p[x];
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		p[i] = i;
		sizee[i] = 1;//最开始的时候每一个集合里只有自己一个点
	}
	while (m--) {
		char op[3];
		int a, b;
		scanf("%s", op);
		if (op[0] == 'C') {//连边,就是合并
			scanf("%d%d", &a, &b);
			if (find(a) == find(b))//如果a和b已经在同一个集合里了,就continue跳出
				continue;
			sizee[find(b)] += sizee[find(a)];//将节点数加到新根节点数上
			p[find(a)] = find(b);//a的根节点的根节点等于b的根节点
		} else if (op[1] == '1') {
			scanf("%d%d", &a, &b);
			if (find(a) == find(b))//判断是或在同一个集合里
				puts("Yes");
			else
				puts("No");
		} else {
			scanf("%d", &a);
			printf("%d\n", sizee[find(a)]);
		}
	}
	return 0;
}

3、食物链:

//余1,可以吃根节点
//余2,可以被根节点吃
//余0,与根节点是同类
#include <iostream>
using namespace std;
const  int N = 50010;
int n, m;
int p[N], d[N];

//p[n]是n的父节点,d[n]是n节点到根节点的距离
int find(int x) {
	if (p[x] != x) { //如果当前节点x不是根节点
		int t  = find(p[x]);//先用t存下来x的根节点
		d[x] += d[p[x]]; //把d[x]更新成x到根节点之间的距离
		p[x] =  t; //再把p[x]更新成x到根节点的距离
	}
	return p[x];
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++)
		p[i] = i;//初始化父节点是自己,d距离初始化都为0
	int res = 0;//记录假话数量
	while (m--) {
		int t, x, y;
		scanf("%d%d%d", &t, &x, &y);
		if (x > n || y > n)
			res++;//如果x,y超出动物总数n,就说明是假话,res++
		else {
			int px = find(x), py = find(y); //先找根节点
			if (t == 1) { //现在x和y是同类
//x和y在同一颗树上,且x到根节点的距离和y到根节点的距离之差模3不等于0,就是假话
				if (px == py && (d[x] - d[y] ) % 3)
					res++;
				else if (px != py) { //如果x和y不在同一颗树上
					p[px] = py;//就把x和y放到同一颗树上
					d[px] = d[y] - d[x];
				}
			} else {//t==2时,表示x吃y
//x和y在同一颗树上,x到根节点的距离应该比y到根节点的距离多1,否则是假话
				if (px == py && (d[x] - d[y] - 1) % 3)
					res++;
				else if (px != py) { //x和y不在同一颗树上
					p[px] = py;//把px更新到py树下,也就是px的父节点更新成py
					d[px] = d[y] + 1 - d[x];
				}
			}
		}
	}
	printf("%d\n", res);
	return 0;
}

总结

以上就是我对这部分的理解,感谢大家的观看谢谢大家!!!

文章来源:https://blog.csdn.net/2301_80882026/article/details/135435948
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。