• 数据结构 并查集


    作用

    快速的处理以下问题:【近乎O(1)的时间完成】

    1.将两个集合合并

    2.询问两个元素是否在一个集合中

    用树的形式维护集合

    基本原理

    每一个集合用一棵树表示

    每一个集合的编号就是根结点的编号,对于每一个结点,都存储其父结点,p[x]表示x的父结点,即p[x]=a表示编号为x的结点的父结点的编号为a

    求某个点属于哪个集合时,就先找其父结点,如果其父结点不是根结点,那么就继续找其父结点的父结点,直到找到其根结点为止

    问题1

    如何判断树根:if(p[x]==x)

    问题2

    如何求x的集合编号:while(p[x]!=x) x=p[x];【只要x不是树根,就一直往上走,直到找到树根为止】

    该步骤时间复杂度仍然很高,需要进行以下优化:

    路径压缩:一旦找到根结点,就会把整个路径上所有点都指向根结点。【基本O(1)】

    安秩合并【一般不用】

    问题3

    如何合并两个集合:px是x集合的集合编号【即x集合的根节点的编号是px】,py是y集合的集合编号【即y集合的根节点的编号是py】 p[px]=py或p[py]=px

    代码实现

    int find(int x) {//返回x所在集合的编号+路径压缩

    if (p[x] != x)

    p[x] = find(p[x]);

    return p[x];

    }

    例题——合并集合

    一共有n个数,编号是1~n,最开始每个数各自在一个集合中。

    现在要进行m个操作,操作共有两种:

    “M a b”,将编号为a和b的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;

    “Q a b”,询问编号为a和b的两个数是否在同一个集合中;

    输入格式

    第一行输入整数n和m。

    接下来m行,每行包含一个操作指令,指令为“M a b”或“Q a b”中的一种。

    输出格式

    对于每个询问指令”Q a b”,都要输出一个结果,如果a和b在同一集合内,则输出“Yes”,否则输出“No”。

    每个结果占一行。

    数据范围

    1≤n,m≤105

    输入样例

    4 5

    M 1 2

    M 3 4

    Q 1 2

    Q 1 3

    Q 3 4

    输出样例

    Yes

    No

    Yes

    代码

    #include

    using namespace std;

    const int N = 100010;

    int p[N];//存储父结点

    int n, m;

    int find(int x) {//返回x所在集合的编号+路径压缩

    if (p[x] != x)

    p[x] = find(p[x]);

    return p[x];

    }

    int main() {

    scanf("%d%d", &n, &m);

    for (int i = 1;i <= n;i++)

    p[i] = i;

    while (m--) {

    char op[2];//因为在使用scanf进行读取时,若读取单个字符会读取到空格、回车等其他字符,使用scanf读取字符串时可忽略空格、回车等其他字符

    int a, b;

    scanf("%s%d%d", op, &a, &b);

    if (op[0] == 'M')

    p[find(a)] = find(b);//合并

    //find(a)返回a的祖宗结点,find(b)返回b的祖宗结点,让a的祖宗结点的父结点等于b的祖宗结点

    else {

    if (find(a) == find(b))

    puts("Yes");

    else

    puts("No");

    }

    }

    return 0;

    }

    例题——连通块中点的数量

    给定一个包含n个点(编号为1~n)的无向图,初始时图中没有边。

    现在要进行m个操作,操作共有三种:

    “C a b”,在点a和点b之间连一条边,a和b可能相等;

    “Q1 a b”,询问点a和点b是否在同一个连通块中,a和b可能相等;

    “Q2 a”,询问点a所在连通块中点的数量;

    输入格式

    第一行输入整数n和m。

    接下来m行,每行包含一个操作指令,指令为“C a b”,“Q1 a b”或“Q2 a”中的一种。

    输出格式

    对于每个询问指令”Q1 a b”,如果a和b在同一个连通块中,则输出“Yes”,否则输出“No”。

    对于每个询问指令“Q2 a”,输出一个整数表示点a所在连通块中点的数量

    每个结果占一行。

    数据范围

    1≤n,m≤10^5

    输入样例

    5 5

    C 1 2

    Q1 1 2

    Q2 1

    C 2 5

    Q2 5

    输出样例

    Yes

    2

    3

    代码

    #include

    using namespace std;

    const int N = 100010;

    int p[N], size1[N];//存储父结点

    int n, m;

    int find(int x) {//返回x所在集合的编号+路径压缩

    if (p[x] != x)

    p[x] = find(p[x]);

    return p[x];

    }

    int main() {

    scanf("%d%d", &n, &m);

    for (int i = 1;i <= n;i++) {

    p[i] = i;

    size1[i] = 1;

    }

    while (m--) {

    char op[5];

    int a, b;

    scanf("%s", op);

    if (op[0] == 'C') {

    scanf("%d%d", &a, &b);

    if (find(a) == find(b))

    continue;

    //特判:如果两个数已经在一个集合,就不用再次合并,不然会使得元素中的元素个数翻倍

    size1[find(b)] += size1[find(a)];//b中元素数量为其本身加上a中元素数量

    p[find(a)] = find(b);//合并,将a合并到b中

    }

    else if (op[1] == '1') {

    scanf("%d%d", &a, &b);

    if (find(a) == find(b))

    puts("Yes");

    else

    puts("No");

    }

    else {

    scanf("%d", &a);

    printf("%d\n", size1[find(a)]);

    }

    }

    return 0;

    }

  • 相关阅读:
    wordpress博客趣主题个人静态网页模板
    Tomcat知识点(深入剖析Tomcat学习笔记)
    MFC|选择获取文件路径
    知网中的硕博论文是caj格式,如何用CAJ转PDF?
    Win10下python的命令行启动和调用问题
    【Python实战】“特种兵”们的专属游戏助手,助你吃鸡:极品小助手也是棒呆了~(“大吉大利,今W吃鸡”)
    本机构(极安御信),就捉迷藏安全剽窃、抄袭我方课程事件在此声明
    Golang 语法入门
    容器多机部署eureka及相关集群服务出现 Request execution failed with message: AuthScheme is null
    应急响应常用命令(Linux)---读书笔记
  • 原文地址:https://blog.csdn.net/weixin_65951505/article/details/134499128