[数据结构与算法-02]并查集

并查集

Pecco大佬的笔记

特点

并查集被很多OIer认为是最简洁而优雅的数据结构之一,主要用于解决一些元素分组的问题。它管理一系列不相交的集合,并支持两种操作:

  • 合并(Union):把两个不相交的集合合并为一个集合。
  • 查询(Find):查询两个元素是否在同一个集合中。

思路

  • 存储/初始化:

    • 每个集合用一个代表元素(称作)表示

      • 这个代表元素必须满足其老大为自身
    • 用一个数组来记录每个元素的老大(不一定是所在集合的代表元素)比如a的老大是b,则表示a属于b所在的集合,而这个集合的代表元素可能为c

    • 每个元素各自为阵,根为自身,每个元素独立构成一个集合

  • 查询(Find):找到元素所在集合的根,递归实现

    • 判断这个元素的老大是否是自身:
      • 若是,返回
      • 若否,找老大的根
  • 合并(Union):让一个元素的根做另一个元素的根的老大

  • 路径压缩:由于查询时要经过所有的上级,可顺便让这个元素的老大直接变为根

  • 按秩合并:为避免树的深度加深,把简单的树(子孙节点少)往复杂的树上合并(子孙节点多),这里用树的深度近似

    • 用一个数组rank[]记录每个节点所对应的树的深度(子节点对应子树的深度)
    • 一开始所有树的rank均为1
    • 合并时将rank小者向rank大者合并
    • rank相同,新的根节点rank要+1
    • 按rank合并会带来额外的空间开销
  • 路径压缩和按秩合并如果一起使用,时间复杂度接近O(n) ,但是很可能会破坏rank的准确性。

代码实现

初始化

inline void init(){
    for (int element = 0; element < n; element++)
        fa[element] = element;
}

查询(路径压缩)

int find(int element){
    if (fa[element] == element)return element;
    else{
        fa[element] = find(fa[element]);
        return father[element];
    }
}

合并(按rank合并)

void union(int eleOne, int eleTwo){
    int eleOneFa = find(eleOne);
    int eleTwoFa = find(eleTwo);
    if (eleOneFa == eleTwoFa);
    else{
        if (rank[eleOneFa] < rank[eleTwoFa])
        	fa[eleOneFa] = eleTwoFa;
        else if (rank[eleOneFa] > rank[eleTwoFa])
            fa[eleTwoFa] = eleTwoFa;
        else{
            fa[eleOneFa] = eleTwoFa;
            rank[eleTwoFa]++;
        }
    }
}

P3367 【模板】并查集

题目描述

如题,现在有一个并查集,你需要完成合并和查询操作。

输入格式

第一行包含两个整数 N,M 表示共有 N 个元素和 M 个操作。

接下来 MM 行,每行包含三个整数 Z_i,X_i,Y_iZ**i,X**i,Y**i

当 Z_i=1Z**i=1 时,将 X_iX**i 与 Y_iY**i 所在的集合合并。

当 Z_i=2Z**i=2 时,输出 X_iX**i 与 Y_iY**i 是否在同一集合内,是的输出 Y ;否则输出 N

输出格式

对于每一个 Z_i=2Z**i=2 的操作,都有一行输出,每行包含一个大写字母,为 Y 或者 N

输入输出样例

输入 #1复制

4 7
2 1 2
1 1 2
2 1 2
1 3 4
2 1 4
1 2 3
2 1 4

输出 #1复制

N
Y
N
Y

说明/提示

对于 30%30% 的数据,N \le 10N≤10,M \le 20M≤20 。

对于 70%70% 的数据,N \le 100N≤100,M \le 10^3M≤103 。

对于 100%100% 的数据,1\le N \le 10^41≤N≤104,1\le M \le 2\times 10^51≤M≤2×105 。

完整解答

#include <cstdio>
#define MAXN 10000
int X, Y, M, N, fa[MAXN + 10];
short op;
void init(int n){
    for (int i = 0; i < n; i++) fa[i] = i;
}
int find(int element){
    if (fa[element] == element)return element;
    else{
        fa[element] = find(fa[element]);
        return fa[element];
    }
}
int Union(int eleOne, int eleTwo){
        fa[find(eleOne)] = find(eleTwo);
}
int main(){
    scanf("%d%d", &N, &M);
    init(N);
    for (int i = 0; i < M; i++){
        scanf("%hd%d%d", &op, &X, &Y);
        if (op == 1){
            Union(X,Y);
        } else if (op == 2){
            if (find(X) == find(Y)){
                printf("Y\n");
            }else{
                printf("N\n");
            }
        }
    }
}
posted @ 2021-02-26 17:35  ChenHongKai  阅读(93)  评论(0)    收藏  举报
1 2 3
4