集合的查、并运算,即简化版运算方法,按秩归并,压缩路径

集合的表示:
1.集合运算:交、并、补、差,判定里两个元素是否属于某一个集合
2.并查集:集合并、查某元素属于什么集合
3.并查集可以用树结构表示,树的每个结点表示一个集合元素
双亲表示法:(孩子指向双亲)
这个树结构采用数组存储形式:
数组每个元素的类型描述为:

1 typedef struct {
2     ElementType Data;
3     int Parent;
4 }SetType; 

集合运算(下面的完整代码中用简化版的查,并运算,体会二者差别)

(1)查找某个元素所在集合(用根节点表示)

1 int Find(SetType s[], ElementType X)
2 {        //在数组s中查找值为x的元素的所属集合
3         //MaxSize是全局变量,为数组s的最大长度
4     int i;
5     for(i = 0; i < MaxSize && S[i].Data != X; i++);
6     if(i >= MaxSize) return -1;            //未找到x,返回-1
7     for(; s[i].parent >= 0; i = s[i].parent);
8     return i;            //找到x所属集合,返回树根节点在数组s中的下标
9 }


(2)集合并运算
分别找到x1, x2两个元素所在集合树的根节点
如果他们不同根,则将其中一个根节点的父节点设置成另一个根节点的数组下标

1 void Union(SetType S[], ElementType x1, ElementType x2 )
2 {
3     int Root1, Root2;
4     Root1 = Find(S, x1);
5     Root2 = Find(S, x2);
6     if(Root1 != Root2)
7         S[Root2].parent = Root1;
8 }

为了改善合并以后的查找性能,可以采用小的集合合并到相对大的集合
用根节点的parent的绝对值来表示集合的结点个数

简化版查、并运算完整代码

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 #define MaxSize 15
 5 typedef int ElementType;    //默认元素用非负数表示
 6 typedef int SetName;        //默认用根节点的下标表示集合名称
 7 typedef ElementType SetType[MaxSize];
 8 
 9 void Initialization(SetType S, int n)
10 {
11     for (int i = 0; i < n; i++)
12         S[i] = -1;
13 }
14 
15 
16 SetName Find(SetType S, ElementType X)
17 {
18     //默认集合元素全部初始化为-1
19     for (; S[X] >= 0; X = S[X]);
20     return X;
21 }
22 
23 
24 void Union(SetType S, SetName root1, SetName root2)
25 {
26     S[root2] = root1;    
27 }
28 
29 void Input_connection(SetType S)
30 {
31     ElementType u, v;
32     SetName root1, root2;
33     scanf_s("%d %d", &u, &v);
34     root1 = Find(S, u - 1);
35     root2 = Find(S, v - 1);
36     if (root1 != root2)
37         Union(S, root1, root2);
38 }
39 
40 void Check_connection(SetType S)
41 {
42     ElementType u, v;
43     SetName root1, root2;
44     scanf_s("%d %d", &u, &v);
45     root1 = Find(S, u - 1);
46     root2 = Find(S, v - 1);
47     if (root1 == root2)
48         printf("yes
");
49     else
50         printf("no
");
51 }
52 
53 //检查是否所有结点都连通了
54 void Check_network(SetType S, int n)
55 {
56     int i, counter = 0;
57     for (i = 0; i < n; i++)
58     if (S[i] < 0) counter++;
59     if (counter == 1)
60         printf("The net work is connected.
");
61     else
62         printf("There are %d components.
", counter);
63 }
64 
65 int main()
66 {
67     SetType S;
68     int n;
69     char in;
70     scanf_s("%d", &n);
71     Initialization(S, n);
72     do{
73 
74         scanf_s("%c", &in);
75         switch (in)
76         {
77         case 'I':
78             Input_connection(S);
79             break;
80         case 'C':
81             Check_connection(S);
82             break;
83         case 'S':
84             Check_network(S, n); 
85             break;
86         }
87     } while (in != 'S');
88     return 0;
89 }

按秩归并(进一步优化程序)

有两种方式

(一)

 1 //按秩归并
 2 void Union(SetType S, SetName root1, SetName root2)
 3 {
 4     /*if (S[root1] < S[root2])        //S[root]中存树的高度
 5     S[root2] = root1;
 6     else
 7     {
 8     if (S[root1] == S[root2])
 9     S[root1]--;
10     S[root1] = root2;
11     }*/
12 }

(二)

 1 void Union(SetType S, SetName root1, SetName root2)
 2 {
 3 
 4     if (S[root2] < S[root1])        //S[root]中存树的结点个数
 5     {
 6         S[root2] += S[root1];
 7         S[root1] = root2;
 8     }
 9     else
10     {
11         S[root1] += S[root2];
12         S[root2] = root1;
13     }
14 
15 
16 }

路径压缩(极大的减少运行时间)

1 //路径压缩    降低了输的高度,使查找过程中访问过的结点都直接指向根节点
2 SetName Find(SetType S, ElementType X)
3 {
4     if (S[X] < 0)
5         return X;
6     else
7         return S[X] = Find(S, S[X]);    //先找到根,把根变成X的父节点,在返回根
8 }