算法笔记--数据结构--并查集

并查集的定义

  • 合并:Union
  • 查找:Find
  • 集合:Set

并查集其实就是一个数组

int father[N];

father[i]表示元素i的父亲结点,而父亲结点本身也是这个集合内的元素(1≤i≤N)

father[i] = i说明元素i是根结点

Snipaste_2020-04-15_17-59-17

并查集的基本操作

初始化

for(int i = 1; i <= N; i++){
    father[i] = i;		// 初始时,每个元素都是独立的一个集合
}

查找

int findFather(int x){
    if(x == father[x]) return x;
    else return Findfather(father[x]);
}

合并

合并是指把两个集合合并成一个集合

步骤

  1. 对于给定的两个元素a和b,判断他们是否属于同一个集合。即调用findFather函数对这两个元素查找根结点,然后再判断其根结点是否相同
  2. 合并两个集合:在上一步中已经获得了两个元素的根结点faA和faB,因此只需要把其中一个的父亲结点指向另一个结点
void Union(int a, int b){
    int faA = findFather(a);
    int faB = findFather(b);
    if(faA != faB){
        father[faA] = faB;
    }
}

为了保证不产生环:只对不同集合进行合并。所以,并查集产生的每一个集合都是一棵树

路径压缩

当许多元素形成一条链时,查找函数的效率就会很低,所以要进行路径压缩

Snipaste_2020-04-15_18-13-12

把当前查询结点的路径上的所有结点的父亲都指向根结点

步骤

  1. 按照原先的写法获得x的根结点r
  2. 重新从x开始走一遍寻找根结点的过程,把路径上经过的所有结点的父亲全部改为根结点r
int findFather(int v){
    if(v == father[v]) return v; // 找到根结点
    else{
        int F = findFather(father[v]);
        father[v] = F;
        return F;
    }
}

例题

Snipaste_2020-04-15_18-24-01

Snipaste_2020-04-15_18-24-09

#include<cstdio>

const int MAXN = 110;
bool isRoot[MAXN];
int father[MAXN];

void initial(int n){
    for(int i = 1; i <= n; i++){
        father[i] = i;
        isRoot[i] = false;
    }
}

int findFather(int x){
    if(x == father[x]) return x;
    else{
        int F = findFather(father[x]);
        father[x] = F;
        return F;
    }
}

void unions(int a, int b){
    int faA = findFather(a);
    int faB = findFather(b);
    if(faA != faB){
        father[faA] = faB;
    }
}

int main(){
    int n, m, a, b;
    scanf("%d%d", &n, &m);
    initial(n);
    for(int i = 0; i < m; i++){
        scanf("%d%d", &a, &b);
        unions(a, b);
    }
    for(int i = 1; i <= n; i++){
        isRoot[findFather(i)] = true;
    }
    int ans = 0;
    for(int i = 1; i <= n; i++){
        ans += isRoot[i];
    }
    printf("%d
", ans);
    return 0;
}

Write by Gqq

原文地址:https://www.cnblogs.com/zgqcn/p/12707252.html