並查集。路徑壓縮 算法運用學習(一)


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\n",getfather(start)==getfather(end)?"Yes":"No");
    }
}

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM