• 使用C语言实现并查集


    1.集合

    数学中集合的定义:具有某种特定性质的具体的或抽象的对象汇总而成的集体。其中,构成集合的这些对象则称为该集合的元素

    集合比如:一个班级的同学;一个生物群落,同样爱好的人等。但是这些集合中之间是不相交的,也就是不属于同一个集合(当然这些大家都明白,只是因为接下来的内容需要,所以提及这个概念)。

     (1)多个集合

     怎么判断集合a中的元素d是属于集合a的呢?

    思路:其实集合中的元素组成的就像一棵树一样,既然是树的话,那么就有一个“根”节点,如果从d->b->a找到a这个根节点的话,就可以判断元素d是否属于集合a了。

    怎么合并两个集合呢?

    思路:只要集合b中的根节点e的父节点改为节点a即可,也就是让一个集合的根节点是另一个结合的根节点。

     (2)代码的实现

    根据上面提到的查找某个节点和合并两个集合的思路,实现其代码:

    并查集的存储
    数组下标数据元素parent
    0a-1
    1b0
    2c0
    3d1
    4e0
    5f4
    6g5
    7h5
    8
    9

    注:由于a节点没有父节点,所以将parent设置为-1,其他的节点同样将父节点都设置为数组下标,比如b节点的父节点为a,而a的数组下标为0.

    1. #include<stdio.h>
    2. #include<stdlib.h>
    3. #define maxn 100
    4. typedef int ElemType;
    5. int parent[maxn];
    6. //初始化
    7. void InitParent(int n){
    8. for(int i=0;i<n;i++){
    9. parent[i]=-1;
    10. }
    11. }
    12. //查找某个节点从属的集合
    13. int find(int x){
    14. while(parent[x]>=0){
    15. x=parent[x];
    16. }
    17. return x;
    18. }
    19. //合并两个集合
    20. void Union(int root1,int root2){
    21. if(root1==root2){
    22. return;
    23. }
    24. parent[root2]=root1;
    25. }

    (3)合并操作的优化

    可是上面存在一个缺点就是,就是在合并的时候,查找的操作find的时间复杂度最坏的情况下为O(n),并且将n个元素进行多次的合并时间复杂度可能达到O(n^2),看下图:

     

     注:可以看到两种合并方式导致树的高度不同,如果树增高太多的话,那么会导致后面查找的时候更加的耗时,为了尽量减小合并之后树的高度,应该将较矮的树合并到较高的树中,这样树的高度不至于太高。

    优化之后的合并代码:使用根节点的绝对值表示树的节点数:

    并查集的合并改进
    数组下标数据元素parent
    0a-4
    1b0
    2c0
    3d1
    4e-4
    5f4
    6g5
    7h5
    8
    9

    a和e对应的parent=-4,表示| parent |=4为树的节点数。

    1. //合并两个集合
    2. void Union_1(int root1,int root2){
    3. if(root1==root2){
    4. return;
    5. }
    6. if(abs(parent[root2])<abs(parent[root1])){
    7. parent[root1]+=parent[root2];
    8. parent[root2]=root1;
    9. }else{
    10. parent[root2]+=parent[root1];
    11. parent[root1]=root2;
    12. }
    13. }

    注:改进之后的find查找时间复杂度最坏为O(log2n),将n个独立的元素为一个集合的合并操作最坏的时间复杂度为O(nlog2n).

    (4)查找操作的优化

    采用路径压缩的方式进行优化:首先找到根节点,再将查找根节点路径上所经过的所有节点都挂到根节点上,这样在查找根节点的时候更少的经过中间的路径,如下图所示:

     

    并查集的查找改进
    数组下标数据元素parent
    0a4
    1b4
    2c0
    3d4
    4e-1
    5f4
    6g5
    7h5
    8
    9

    这样在查找节点b,d的时候可以直接查找到根节点e了。

    1. //查找某个节点从属的集合
    2. int find_1(int x){
    3. int root=x;
    4. //首先找到根节点
    5. while(parent[x]>=0){
    6. root=parent[root];
    7. }
    8. //如果x不能直接找到根节点,那么需要对其经过路径上的
    9. //所有节点直接指向根节点进行路径的压缩
    10. if(x!=root){
    11. int t=parent[x];
    12. parent[t]=root;
    13. x=t;
    14. }
    15. return root;
    16. }

    注:改进之后的find查找时间复杂度为O(a(n)),将n个独立的元素为一个集合的合并操作的时间复杂度为O(n*a(n)).

    PTA团体程序设计天梯赛-L2-024 部落

    poj1703

    poj2524

    poj1182

    HDU1213

    HDU1232

  • 相关阅读:
    [Vue项目实战]尚品汇 -- 初始化项目以及项目的配置与分析
    MyBatis select标签的简介说明
    LoadBalance客户端负载均衡
    JS中执行上下文和执行栈是什么?
    PIL或Pillow学习1
    python常用数据结构-元组
    WPF中使用LibVLCSharp.WPF 播放rtsp
    【Seata】04 - Seata TCC 模式 Demo 调用流程分析
    MSP432学习笔记7:定时器A中断
    MySQL InnoDB引擎优势以及共享表空间扩容和日志文件详解
  • 原文地址:https://blog.csdn.net/Keep_Trying_Go/article/details/126427641