• 并查集


    一、定义

    并查集是一种树形的数据结构,用于处理一些不相交集合的合并以及查询问题。

    二、操作

    1、void make_set(int n)

    含义:有n个元素,把这n个元素初始化成n个集合,每个集合包含1个元素。

    2、int find_root(int x)

    含义:查找元素x所在的集合,返回集合的根结点。通过查找两个元素的根结点是否相同,可以判断两个元素是否属于同一个集合。

    3、void union_set(int a, int b)

    含义:合并两个不相交的集合。

    三、举例

    假设有3个元素,编号分别为1,2,3,则进行初始化make_set后,这3个元素形成了3个集合{1},{2},{3},集合之间互不相交,对应的树如下图所示:

    可以看到3个元素形成了3颗独立的树,每颗树只包含一个根结点。我们使用数组p[x]来表示结点x的父结点,若x为根结点,则令p[x]=-1(或者令p[x]=x),所以有p[1]=-1,p[2]=-1,p[3]=-1。合并集合则是将一棵树的根结点变成另一颗树的孩子结点,合并集合{1},{2}后,对应的数如下所示:

    此时有p[1]=-1,p[2]=1,p[3]=-1。再将集合{3}与集合{1,2}合并,则有

    此时p[1]=-1,普p[2]=1,p[3]=1,3个集合最终合并成了一个集合。

    四、优化

    并查集最初的状态时一个个不相交的集合,如果只是简单的合并而不采取任何优化,那么树高可能会不断增加,最坏的情况是一棵树退化成了一条链,树高越高,查找根结点所花费的时间也就越长,我们希望树应该尽可能的低(两层),可以使用下面两种方法来进行优化:

    1、路径压缩

    在查找某结点x的根结点的过程中,将结点x与根结点和x之间的所有结点都直接指向根结点,这就是路径压缩。如下图:

    代码实现(递归):

    //查找根结点时路径压缩,递归实现
    int find_root(int x)
    {
        if(p[x] == -1)
            return x;
        else 
        {
            int t = find_root(p[x]);
            p[x] = t;
            return t;
        }
    }

    代码实现(非递归):

    //查找根结点时路径压缩,非递归实现
    int find(int x)
    {
        int k, j, r;
        r = x;
        while(r != p[r])     //查找跟节点
            r = p[r];      //找到跟节点,用r记录下
        k = x;        
        while(k != r)             //非递归路径压缩操作
        {
            j = [k];         //用j暂存parent[k]的父节点
            p[k] = r;        //parent[x]指向跟节点
            k = j;                    //k移到父节点
        }
        return r;         //返回根节点的值            
    }

    2、按秩合并

    按秩合并即将元素少的集合合并到元素多的集合中,这样有利于降低树高。

    五、代码实现

    假设有n个元素,编号为1~n,现在输入这n个元素中的m个元素,输出最终建立的树中每个结点的父结点。

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <vector>
     4 using namespace std;
     5 
     6 const int N = 1000;
     7 int p[N];
     8 vector<int> v;  //存储输入的元素编号
     9 
    10 int make_set(int n)
    11 {
    12     for(int i=1; i<=n; i++)
    13         p[i] = -1;  //初始化,每个结点为一棵树
    14 }
    15 
    16 int find_root(int x)
    17 {
    18     if(p[x] == -1)
    19         return x;
    20     else
    21     {
    22         int t = find_root(p[x]);    //路径压缩
    23         p[x] = t;
    24         return t;
    25     }
    26 }
    27 
    28 void union_set(int a, int b)
    29 {
    30     int ra = find_root(a);
    31     int rb = find_root(b);
    32 
    33     if(ra != rb)    //a,b不在同一集合中,合并
    34         p[ra] = rb;
    35 }
    36 
    37 int main()
    38 {
    39     int n, m;
    40     scanf("%d%d",&n,&m);
    41     make_set(n);
    42     int a, b;
    43     scanf("%d", &a);
    44     v.push_back(a);
    45     for(int i=1;i<m;i++)
    46     {
    47         scanf("%d", &b);
    48         v.push_back(b);
    49         union_set(a, b);
    50         a = b;
    51     }
    52 
    53     for(int i=0; i<v.size(); i++)
    54         printf("p[%d]=%d
    ", v[i], p[v[i]]);
    55     return 0;
    56 }

    输入:

    3 3
    1 2 3

    输出:

    p[1]=2
    p[2]=3
    p[3]=-1

    六、带权并查集

    普通并查集只包含了结点与结点之间是否属于同一集合的信息,而带权并查集则加入了一个数组r[x],记录了结点x与其父结点之间的信息(通常情况下),比如是否是同一类,有了多余的信息r[x]相当于结点x有了权值,这也是“带权”的含义。在进行路径压缩find_set和合并union_set时,r[]也要进行相应的变化,按秩合并就是带权并查集的一个应用。

    七、例题

    普通并查集:poj1611poj2524hdoj1232

    带权并查集:poj2492poj1703poj1182

  • 相关阅读:
    SAP CRM呼叫中心和Hybris Backoffice的轮询设计
    SAP Hybris和Netweaver的集群模式以及集群模式下工作需要解决的问题
    Hybris Commerce的 Master Tenant和Netweaver的System Client
    使用代码创建SAP CRM服务请求文档的subject测试数据
    SAP CRM中间件下载出错的错误排查
    PHP 异常处理 throw new exception
    使用mysql乐观锁解决并发问题思路
    gatewayworker中使用tcp协议连接硬件设备获取数据报错解决办法!
    trycatche
    PHP(Mysql/Redis)消息队列的介绍及应用场景案例--转载
  • 原文地址:https://www.cnblogs.com/sench/p/7953768.html
Copyright © 2020-2023  润新知