并查集是一种数据结构,一般用来解决连通性或者动态连通性的问题。
动态连通性:
即一个图有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. "); 53 else if((r[a]+r[b])%2!=0)//画个图就推出来了 54 printf("In different gangs. "); 55 else 56 printf("In the same gang. "); 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 }