zoukankan      html  css  js  c++  java
  • 并查集。路径压缩 算法运用学习(一)

    Description

    Bo has been in Changsha for four years. However he spends most of his time staying his small dormitory. One day he decides to get out of the dormitory and see the beautiful city. So he asks to Xi to know whether he can get to another bus station from a bus station. Xi is not a good man   because he doesn’t tell Bo directly. He tells to Bo about some buses’ routes. Now Bo turns to you and he hopes you to tell him whether he can get to another bus station from a bus station directly according to the Xi’s information.

    Input

    The first line of the input contains a single integer T (0<T<30) which is the number of test cases. For each test case, the first contains two different numbers representing the starting station and the ending station that Bo asks. The second line is the number n (0<n<=50) of buses’ routes which Xi tells. For each of the following n lines, the first number m (2<=m<= 100) which stands for the number of bus station in the bus’ route. The remaining m numbers represents the m bus station. All of the bus stations are represented by a number, which is between 0 and 100.So you can think that there are only 100 bus stations in Changsha.

    Output

    For each test case, output the “Yes” if Bo can get to the ending station from the starting station by taking some of buses which Xi tells. Otherwise output “No”. One line per each case. Quotes should not be included.

    Sample Input

    3
    0 3
    3
    3 1 2 3
    3 4 5 6
    3 1 5 6
    0 4
    2
    3 0 2 3
    2 2 4
    3 2
    1
    4 2 1 0 3
    

    Sample Output

    No
    Yes
    Yes




    复杂度分析

    空间复杂度为O(N),建立一个集合的时间复杂度为O(1),N次合并M查找的时间复杂度为O(M Alpha(N)),这里Alpha是Ackerman函数的某个反函数,在很大的范围内(人类目前观测到的宇宙范围估算有10的80次方个原子,这小于前面所说的范围)这个函数的值可以看成是不大于4的,所以并查集的操作可以看作是线性的。


    基本操作

    并查集是一种非常简单的数据结构,它主要涉及两个基本操作,分别为:

    A. 合并两个不相交集合

    B. 判断两个元素是否属于同一个集合

    (1)       合并两个不相交集合(Union(x,y))

    合并操作很简单:先设置一个数组Father[x],表示x的“父亲”的编号。那么,合并两个不相交集合的方法就是,找到其中一个集合最父亲的父亲(也就是最久远的祖先),将另外一个集合的最久远的祖先的父亲指向它。

    图为两个不相交集合,b图为合并后Father(b):=Father(g)

    (2)       判断两个元素是否属于同一集合(Find_Set(x))

    本操作可转换为寻找两个元素的最久远祖先是否相同。可以采用递归实现。


    优化

    (1) Find_Set(x)时,路径压缩

    寻找祖先时,我们一般采用递归查找,但是当元素很多亦或是整棵树变为一条链时,每次Find_Set(x)都是O(n)的复杂度。为了避免这种情况,我们需对路径进行压缩,即当我们经过”递推”找到祖先节点后,”回溯”的时候顺便将它的子孙节点都直接指向祖先,这样以后再次Find_Set(x)时复杂度就变成O(1)了,如下图所示。可见,路径压缩方便了以后的查找。

    (2) Union(x,y)时,按秩合并

    即合并的时候将元素少的集合合并到元素多的集合中,这样合并之后树的高度会相对较小。







    
    

    应用

    并查集常作为另一种复杂的数据结构或者算法的存储结构。常见的应用有:求无向图的连通分量个数,最近公共祖先(LCA),带限制的作业排序,实现Kruskar算法求最小生成树等




    翻译

    
    
    #include<stdio.h>
    #define N 105
    int father[N];
    int a[N];
    void init(int m){
        for(int i=0;i<m;i++)   //初始化 
        father[i]=i;
    }
    int getfather(int x){  //获取根节
        while(x!=father[x])
        x=father[x];
        return x;
    }

    /*
    int getfather(int x) {
    if(x != father[x])
    father[x] = getfather(father[x]); // 路径压缩
    return father[x];
    } */

    bool same(int x,int y){  //判断是否相同 
        return getfather(x)==getfather(y);
    }
    void unions(int x,int y){  //并集合 
       x=getfather(x);
       y=getfather(y);
       if(x!=y) father[x]=y;
           
    }
    int main(){
        int T;
        scanf("%d",&T);
        while(T--){
            init(100);
            int start,end,n;
            scanf("%d%d",&start,&end);
            scanf("%d",&n);
            while(n--){
                int m;
                scanf("%d",&m);
                for(int i=0;i<m;i++) scanf("%d",&a[i]);
                for(int i=1;i<m;i++) unions(a[i-1],a[i]);
            }
            printf("%s
    ",getfather(start)==getfather(end)?"Yes":"No");
        }
    }


  • 相关阅读:
    static 关键字
    gitlab 配置到jenkins
    Eclipse下代码字体背景变红/变绿/变黄原因
    构造方法(和python初始化变量类似)
    面向对象(实际就像python跳用自己写的库那样)
    Python 的AES加密与解密
    break 和 continue 的用法
    for循环
    Eclipse快捷键
    java三元运算符
  • 原文地址:https://www.cnblogs.com/tk55/p/6430704.html
Copyright © 2011-2022 走看看