加边的无向图 题解——并查集图文详解

本来F题选的是一道线段树+懒标记的题
题意是,有许多个点,有一些点之间是互相联通的,有些点之间是不联通的(如图)
在这里插入图片描述
图中的点分成了三块,显然,我们只需要加入两条路,即可将这三个区块的点合为一块
也就是说,我们只要确定这些点分成了N块,就只需要加上N-1条路就能将他们连接在一起
所以就需要一种数据结构-----并查集
将上图中的点标上号在这里插入图片描述
我们开一个数组,其中各个数是
在这里插入图片描述
1号位的数是1,说明1号位的"源点"或者称中心点为1
听不懂?看接下来的模拟过程
如上图,1点和3点之间有一条路,也就是说1,3点是联通的
我们设3点作为源点,于是将1号位上的数字指向三号位,此刻数组变为在这里插入图片描述
接下来2号点与3号点也有一条路,这次我们把2号点作为3号点的源点,数组变为在这里插入图片描述
在这里插入图片描述

1号位的源点是3,3号位的源点是2,这样就能找到1号位的祖宗源点是2
如何用代码来实现寻找:

int find(int x){
	if(x!=p[x])p[x]=find(p[x]);
	return p[x];
}

假如我们要找1的源点
p[x]=3,x=1,所以执行p[x]=find(p[x])语句
递归到下一层,find(p[1])就是find(3),而p[3]!=3,因为p[3]=2,所以再递归一层,find(p[3])就是find(2),此时p[2]=2,成立,返回2,上一层的p[3]=find(p[3])即获得赋值2,返回p[3]再向上传递,p[1]=2,此时向主函数返回2,即是1号点的源点2;
而经过一次find之后,数组此刻变化为在这里插入图片描述
不仅完成了寻找源点,还完成了路径的优化!寻找1的源点的路径从之前的
1->3->2变成了1->2,下一次寻找时所递归的层数就是大大减少
利用并查集,我们可以将所有点区分开来,完成时数组为在这里插入图片描述
此时我们只需要看其中有几个不相同的数即可判断有几个区块

#include <iostream>
#include <set>
using namespace std;
const int N = 100010;
int p[N];

int find(int x) {
	if (x != p[x])p[x] = find(p[x]);
	return p[x];
}

int main() {
	set<int>s;//set的特性是会自动去重
	int n, m, i, j;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		p[i] = i;
	for (int k = 1; k <= n; k++) {
		cin >> i >> j;
		int ii = find(i), jj = find(j);//分别找出i和j的源点
		if (ii != jj) {
			p[ii] = jj;//将i节点的源点指向j节点的源点
		}
	}
	for (int i = 1; i <= n; i++) {
		s.insert(find(i));
	}
	cout << s.size() - 1 << endl;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值