并查集

并查集是一种数据结构,一般用来解决连通性或者动态连通性的问题。

动态连通性:

即一个图有n个结点,不断加入边,问此时任意两个结点的连通性。

建模思路:

对于连通的结点它们属于一个组,不连通的结点就属于不同的组。对于每一条边的输入,

判断两个结点是否连通,如果不连通则将两个结点所属的两个组连成一个组。对于每一个组,设置一个组标志。

初始时,任意两个结点不连通,组标志就是自己。

for(i=0; i<n; ++i)
    father[i] = i;

组的体现形式:

连通的结点属于一个组,那么组内是怎么体现的呢?

是用树来实现的

左边一个组,组的标志是a,右边一个组,组的标志是e

然后father[b] = a,father[c] = a, father[d] = d;

如果每个结点的终极祖先相同,那么就是一个组。
查找结点属于哪一个组的算法find

1 int find(int x)
2 {
3     if(x==parent[x]) return x;//如果自己的父亲是自己,那么就是这个组的标志
4     return find(parent[x]);//否则,看父亲的父亲是不是组的标志
5 }

如果输入一条边u-->v。如果u和v不属于一个组,那么就把两个组变成一个组

1 int Union(int u, int v)
2 {
3     int ru = find(u);
4     int rv = find(v);
5     if(ru != rv)
6     {
7         parent[ru] = rv;
8     }
9 }


但是会发生一个问题,那就是极端下,树的高度可能会很极端

所以要进行路径压缩,即在find()函数返回的过程中,修改father数组

 

1 int find(int x)
2 {
3     if(x==parent[x]) return x;//如果自己的父亲是自己,那么就是这个组的标志
4     return parent[x] = find(parent[x]);//返回的过程中修改father数组,路径压缩
5 }

 

可是仍然存在问题,如果Union很多次,才find一次,那么极端情况下,情况仍然不理想,所以就存在按秩合并

低的树指向高的树,不会增加书高,即按秩合并

 1 int Union(int u, int v)
 2 {
 3     int ru = find(u);
 4     int rv = find(v);
 5     if(ru != rv)
 6     {
 7         if(rank[ru] <= rank[rv])
 8         {
 9             parent[ru] = rv;
10             if(rank[ru]==rank[rv])
11                 rank[rv]++;
12         }
13         else
14             parent[rv] = ru;
15     }
16 }

 

种类并查集题目:

http://poj.org/problem?id=1703

 1 /*
 2 有两个犯罪团伙,即两个种类。
 3 当出现 D A B 时,如果A,B不在一个集合内,那就合并A,B所在的集合,
 4 并且修改r数组
 5 r[x] = 0 表示x和father[x] 是同一种类  
 6 r[x] = 1 表示x和father[x] 不是同一种类
 7 */
 8 #include <stdio.h>
 9 #include <string.h>
10 const int N = 100000 + 10;
11 int father[N];
12 int r[N];
13 void init(int n)
14 {
15     for(int i=1; i<=n; ++i)
16     {
17         father[i] = i;
18         r[i] = 0;
19     }    
20     
21 }
22 int find(int x)
23 {
24     if(x==father[x]) return x;
25     else
26     {
27         int t = find(father[x]);
28         r[x] = (r[x] + r[father[x]]) % 2;//路径压缩时,要修改r数组。画个图,很好推
29         father[x] = t;
30         return t;
31     }
32 }
33 int main()
34 {
35     int n,m,i,a,b,fa,fb,t;
36     char op;
37     scanf("%d",&t);
38     while(t--)
39     {
40         scanf("%d%d",&n,&m);
41         init(n);
42         for(i=0; i<m; ++i)
43         {
44             getchar();
45             op = getchar();
46             scanf("%d%d",&a,&b);
47             
48             fa = find(a); fb = find(b);
49             if(op=='A')
50             {
51                 if(fa!=fb)
52                     printf("Not sure yet.\n");
53                 else if((r[a]+r[b])%2!=0)//画个图就推出来了
54                     printf("In different gangs.\n");
55                 else
56                     printf("In the same gang.\n");
57             }
58             else
59             {
60                 
61                 if(fa!=fb)
62                 {
63                     father[fa] = fb;
64                 }    r[fa] = (r[a] + r[b] + 1)%2;//画个图就推出来了
65             }
66         }
67     }
68     return 0;
69 }

 

posted @ 2015-02-17 20:21  justPassBy  阅读(195)  评论(0编辑  收藏  举报