• 并查集


    参考文章:http://blog.csdn.net/dellaserss/article/details/7724401/

    定义

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

    举例

    杭电1232畅通工程

    首先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的。最后要解决的是整幅图的连通性问题。比如随意给你两个点,让你判断它们是否连通,或者问你整幅图一共有几个连通分支,也就是被分成了几个互相独立的块。像畅通工程这题,问还需要修几条路,实质就是求有几个连通分支。如果是1个连通分支,说明整幅图上的点都连起来了,不用再修路了;如果是2个连通分支,则只要再修1条路,从两个分支中各选一个点,把它们连起来,那么所有的点都是连起来的了;如果是3个连通分支,则只要再修两条路……

    以下面这组数据输入数据来说明

    4 2 1 3 4 3

    第一行告诉你,一共有4个点,2条路。下面两行告诉你,1、3之间有条路,4、3之间有条路。那么整幅图就被分成了1-3-4和2两部分。只要再加一条路,把2和其他任意一个点连起来,畅通工程就实现了,那么这个这组数据的输出结果就是1。好了,现在编程实现这个功能吧,城镇有几百个,路有不知道多少条,而且可能有回路。 这可如何是好?

    实现思路

    int pre[1000 ];
    int
    Find(int x) { int r=x; while(r!=pre[r]) r=pre[r]; return r; }
    void join(int x,int y)  
    {  
        int fx=Find(x),fy=Find(y);  
        if(fx!=fy)  
        {  
            pre[fy]=fx;  
        }  
    }   

    并查集由一个整数型的数组和两个函数构成。数组pre[]记录了每个点的前导点是什么,函数find是查找,join是合并。

    但是这么实现有一个缺点:最后的树状结构会变成什么样,我也完全无法预计,一字长蛇阵也有可能。这样查找的效率就会比较低下。最理想的情况就是所有人的直接上级都是根节点,一共就两级结构,只要找一次就找到根节点了。哪怕不能完全做到,也最好尽量接近。这样就产生了路径压缩算法,查找的时候动态优化树结构。 修改后的查找算法如下:

    int Find(int x)  
    {  
        int r=x;  
        while(r!=pre[r])  
            r=pre[r];  
        
        //路径压缩  
        int i=x,j;  
        while(pre[i]!=r)  
        {  
            j=pre[i];  
            pre[i]=r;  
            i=j;  
        }  
        return r;  
    }  

    杭电1232畅通工程   完整答案

     1     #include<iostream>  
     2     using namespace std;  
     3       
     4     int  pre[1050];  
     5     bool t[1050];               //t 用于标记独立块的根结点  
     6       
     7     int Find(int x)  
     8     {  
     9         int r=x;  
    10         while(r!=pre[r])  
    11             r=pre[r];  
    12           
    13         int i=x,j;  
    14         while(pre[i]!=r)  
    15         {  
    16             j=pre[i];  
    17             pre[i]=r;  
    18             i=j;  
    19         }  
    20         return r;  
    21     }  
    22       
    23     void mix(int x,int y)  
    24     {  
    25         int fx=Find(x),fy=Find(y);  
    26         if(fx!=fy)  
    27         {  
    28             pre[fy]=fx;  
    29         }  
    30     }   
    31       
    32     int main()  
    33     {  
    34         int N,M,a,b,i,j,ans;  
    35         while(scanf("%d%d",&N,&M)&&N)  
    36         {  
    37             for(i=1;i<=N;i++)          //初始化   
    38                 pre[i]=i;  
    39               
    40             for(i=1;i<=M;i++)          //吸收并整理数据   
    41             {  
    42                 scanf("%d%d",&a,&b);  
    43                 mix(a,b);  
    44             }  
    45               
    46               
    47             memset(t,0,sizeof(t));  
    48             for(i=1;i<=N;i++)          //标记根结点  
    49             {  
    50                 t[Find(i)]=1;  
    51             }  
    52             for(ans=0,i=1;i<=N;i++)  
    53                 if(t[i])  
    54                     ans++;  
    55                       
    56             printf("%d
    ",ans-1);  
    57               
    58         }  
    59         return 0;  
    60     }//dellaserss  

    原文思路更加详细与清晰,如有疑惑推荐阅读原文

  • 相关阅读:
    JavaScript数据类型和变量学习小记
    如何在windows下安装Python的PIL库
    maoguy的第一条博客
    Ⅲ. 通过git管理github上托管的代码
    Ⅱ. Git的本地操作--下(不会使github远程仓库发生任何改变)
    Ⅰ. Git的本地操作--上(不会使github远程仓库发生任何改变)
    Linux下Vi/Vim编辑器使用案例(基于Ubuntu)
    Selenium+python上传本地文件或者图片
    Selenium+python操作id为动态变化的frame(iframe)
    Quick-cocos2d-x v3.3 SocketTCP链接(转)
  • 原文地址:https://www.cnblogs.com/ouym/p/7541943.html
Copyright © 2020-2023  润新知