算法笔记-并查集(路径压缩+按秩合并)

资料

  1. CSDN图解
  2. 知乎图解

例题

例题链接
题目背景

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。

题目描述

规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。

输入格式

第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。

以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Mi和Mj具有亲戚关系。

接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。

输出格式

P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。

测试数据

6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6

在这里插入图片描述
在这里插入图片描述

题解(模板)

#include<iostream>
using namespace std;

#define N 1000
int parent[N], depth[N];


//初始化并查集
void init(int n) {
	for (int i = 0; i <= n; i++) {
		parent[i] = i;	// 初始定义各个结点都为根节点
		depth[i] = 1;	// 初始定义树的深度都为1(即树结点数量)
	}
}
// **********优化点二**********
// 不优化的find
//int find(int i) {
//	while (i != parent[i]) {
//		i = parent[i];	// 向上找父结点
//	}
//	return i;
//}

// 使用方式一优化,将查找节点直接链接到根节点上
//int find(int i) {
//	if (i != parent[i]) {
//		parent[i] = find(parent[i]);	// 递归的一个过程
//	}
//	return parent[i];
//}



// 使用方式二优化  不断将父亲节点修改为爷爷节点
//int find(int i) {
//	while (i != parent[i]) {
//		parent[i] = parent[parent[i]];
//		i = parent[i];	// 向上找父结点
//	}
//	return i;
//}
//可简写成:
int find(int i){
    return i == parent[i] ? i : (parent[i] = find(parent[i]));
}


bool connected(int p, int q) {
	p = find(p);
	q = find(q);
	return p == q;
}


//按秩合并
void unionNode(int p, int q) {
	p = find(p);
	q = find(q);
	// **********优化点一**********
	if (p == q)	// 根节点相同
		return;
	else if (depth[p] > depth[q]) {
		parent[q] = p;	// p的深度更大,将q作为子树
	}
	else if(depth[p] < depth[q]){
		parent[p] = q;	// q的深度更大,将p作为子树
	}
	else {
		parent[q] = p;	// 两棵树的深度相同,随意合并 ,但是此时需要更新树的深度
		depth[p]++;
	}
}



int main() {
	int n, m, p, temp1, temp2;
	cin >> n >> m >> p;
	init(n);//初始化并查集
	for (int i = 0; i < m; i++) {
		cin >> temp1 >> temp2;
		unionNode(temp1, temp2);//合并
	}
	for (int i = 0; i < p; i++) {
		cin >> temp1 >> temp2;
		if(connected(temp1, temp2))//查找
			cout << "Yes" << endl;
		else
			cout << "No" << endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

StarAndroid

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值