并查集板子
链接:https://ac.nowcoder.com/acm/problem/14685
来源:牛客网
题目描述
给你一个 n 个点,m 条边的无向图,求至少要在这个的基础上加多少条无向边使得任意两个点可达~
输入描述:
第一行两个正整数 n 和 m 。
接下来的m行中,每行两个正整数 i 、 j ,表示点i与点j之间有一条无向道路。
输出描述:
输出一个整数,表示答案
示例1
输入
复制
4 2
1 2
3 4
输出
复制
1
备注:
对于100%的数据,有n,m<=100000。
#include<iostream>
#include<stdio.h>
using namespace std;
int uset[100005]; // uset[i] 表示i的根节点
int dep[100005]; // 集合数的秩(高度)
// 并查集
void init_uset(int n){
// 将前n个节点的的父节点设置为自己,表示为这个集合中只有自己,
for(int i = 1; i <= n; i++){
uset[i] = i;
}
}
int find_x(int x){
// 查找根节点,并压缩路径(找到x的根节点后,直接将x连接到根节点既uset[i] = 根节点编号)
if(x != uset[x]){
uset[x] = find_x(uset[x]);
}
return uset[x];
}
void union_set(int x, int y){
// 根据秩合并集合
x = find_x(x);
y = find_x(y);
if(x == y){
// 如果根节点相同,则不用合并
return ;
}
if(dep[x] > dep[y]){ //将秩更小的树(高度更低)的根节点,作为更大的秩的树的根节点的子节点进行合并
uset[y] = x;
}else{
uset[x] = y;
if(dep[x] == dep[y]){
dep[y] ++;
}
}
}
int main()
{
int n,m;
cin >> n >> m;
init_uset(n);
int a,b;
for(int i = 0; i < m; i++){
scanf("%d%d", &a, &b);
union_set(a, b);
}
int ans = 0;
for(int i = 1; i <= n; i++){
if(uset[i] == i){ // 根节点的的父节点为自己,所以有几个uset[i] == i表示有几棵树
ans ++;
}
}
cout << ans - 1 << endl;
}