IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    ACM-并查集-结构体/数组-实现

    Kingfish404发表于 2020-02-05 00:00:00
    love 0

    并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。

    算法思想

    用集合中的某个元素来代表这个集合,,该元素称之为集合的代表元

    每个集合可以理解为一个树,对于集合中的每个元素(如x),都有一个值(如parent[x])指向其在结构上的父节点。如果x为集合的代表元,即根节点,则令parent[x]=x;

    对于查找操作,假设需要确定x所在的的集合,也就是确定集合的代表元。可以沿着parent[x]不断在树形结构中向上移动,直到到达根节点。

    判断两个元素是否属于同一集合,只需要看他们的代表元是否相同即可。

    并查集的三个操作

    • 初始化
    • 查找
    • 合并集合

    初始化

    包括对所有单个的数据建立一个单独的集合(即根据题目的意思自己建立的最多可能有的集合,为下面的合并查找操作提供操作对象)

    结构体表示法

    #define MAX 10000
    struct Node
    {
        int parent; // 集合index的类别
        int data;   // 集合index的数据类型
        int rank;   // 集合index的层次,通常初始化为0
     }node[MAX];
    
    // 初始化i集合的函数
     void init(int i){
         node[i].parent=i;  // 初始化的时候,一个集合的parent都是这个集合自己的标号。
                            // 没有跟它同类的集合,那么这个集合的源头只能是自己了。
         node[i].rank=0;
     }
    

    数组表示法

    int parent[max];
    int rank[max];
    int data[max];
    
    void init(int i)
    {
        set[i]=i;
        rank[i]=0;
    }
    

    查找

    结构体

    /**
    *查找集合i(一个元素是一个集合)的源头(递归实现)。
     如果集合i的父亲是自己,说明自己就是源头,返回自己的标号;
     否则查找集合i的父亲的源头。
    **/
    int get_Parent(int x)
    {
        if(node[x].parent==x)
        {
            return x;
        }
        // 在进行查找时,顺便压缩路径
        node[x].parent = get_Parent(node[x].parent);
        return node[x].parent;
    }
    

    数组

    // 查找集合i(一个元素是一个集合)的源头(递归实现)
    int find_set(int i)
    { 
        // 如果集合i的父亲是自己,说明自己就是源头,返回自己的标号
       if(set[i]==i)
       {
           return set[i];
       }
        // 否则查找集合i的父亲的源头
        return  find_set(set[i]);        
    }
    
    // 查找的同时进行集合的优化的函数(减少树的高度)
    int unifind(int a){
        
        int root = a;
        
        // 找到根节点
        while(root != parent[root] ){
            root = parent[root];
        }
        
        // compress the path
        while( a != root){
            int parentOfA = parent[a];
            parent[a] = root; // 将当前节点的父节点直接设置为父节点
            a = parentOfA;
        }
        return root;
    }
    

    合并集合

    Talk is cheep,show the code.

    这里在合并时按照秩进行了路径压缩,将秩较小的树合并到大的上。

    结构体

    void Union(int a,int b)
    {
        a=get_parent(a);
        b=get_parent(b);
        if(node[a].rank>node[b].rank)
            node[b].parent=a;
        else
        {    
            node[a].parent=b;
            if(node[a].rank==node[b].rank)
            {
                node[b].rank++;
            }
        }
    }
    

    数组

    void Union(int i,int j)
    {
        i=Find_Set(i);
        j=Find_Set(j);
        if(i==j) return ;
        if(rank[i]>rank[j])
        {
            set[j]=i;
        }
        else
        {
            if(rank[i]==rank[j])
            {
                rank[j]++;
            }
            set[i]=j;
        }
    }
    

    集合数统计

    计算最后有多少元素父元素仍然为自己parent[x]==x,就算出有多少个不相交的集合

    // 这里只放结构体的函数了
    int count(int i){   // i为有效元素数目
        int c=0;
        while(i--){
            if(node[i].parent==i){
                c++;
            }
        }
        return c;
    }
    

    ACM典型例题:acm_hdu_1213

    Reference

    1. 傻子都能看懂的并查集入门-Ocean
    2. wiki - 并查集


沪ICP备19023445号-2号
友情链接