算法-并查集

并查集板子

链接: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;
}
posted @ 2020-12-16 21:17  没尾巴的刺刺鱼  阅读(105)  评论(0编辑  收藏  举报