1 /*********************************************************
2 * Name: Kruskal算法构造最小生成树
3 * Date: 2022.01.26
4 * Author: 吕辉
5 * Description: 给定无向网,通过Kruskal算法构造出最小生成树。
6 * 最小生成树采用类似并查集存储结构。
7 *********************************************************/
8 #define _CRT_SECURE_NO_WARNINGS
9 #include <stdio.h>
10 #include <stdlib.h>
11
12 typedef struct
13 {
14 char data;
15 int parent;
16 int weight;
17 }MFSetNode;/*并查集结点*/
18
19 typedef struct
20 {
21 int vexnum;
22 MFSetNode* head;
23 }MFSet;/*并查集*/
24
25 typedef struct
26 {
27 char vex1;
28 char vex2;
29 int weight;
30 }ArcNode;/*边结点*/
31
32 typedef struct
33 {
34 int arcnum;
35 ArcNode* head;
36 }Arc;/*边表*/
37
38 void Kruskal(Arc* E, MFSet* T);
39 /*并查集相关函数*/
40 void InitMFSet(MFSet* T);
41 int FindMFSet(MFSet* T, int vex);
42 void UnionMFSet(MFSet* T, int vex1, int vex2, int weight);
43 void InversionMFSet(MFSet* T, int vex);
44 void PrintMFSet(MFSet T);
45 /*边表相关函数*/
46 void InitArc(Arc* E);
47 void BubbleSortArc(Arc* E);
48 int LocateVex(MFSet* T, char vexdata);
49
50 int main(void)
51 {
52 Arc E;/*边表*/
53 MFSet T;/*并查集树*/
54 printf("请输入顶点数和边数(逗号分隔):");
55 scanf("%d%*c%d", &T.vexnum, &E.arcnum);
56 Kruskal(&E, &T);
57 PrintMFSet(T);
58 system("pause");
59 return 0;
60 }
61 /***********************************************
62 * Name: Kruskal
63 * Parameter: E 边表, T 并查集
64 * Call: InitMFSet, InitArc, BubbleSortArc,
65 * FindMFSet, UnionMFSet.
66 * Called By: main
67 * Description: 第一步:按权值从小到大排序边表;
68 * 第二步:选择不会产生环路的边;
69 * 第三步:将其加入最小生成树中。
70 ************************************************/
71 void Kruskal(Arc* E, MFSet* T)
72 {
73 int i = 0;
74 int e = 0;
75 int vex1 = 0;
76 int vex2 = 0;
77 int root1 = 0;
78 int root2 = 0;
79 int weight = 0;
80
81 InitMFSet(T);
82 InitArc(E);
83 BubbleSortArc(E);
84 while (e < T->vexnum - 1)
85 {
86 /*字符型元素转换为并查集中对应元素下标*/
87 weight = E->head[i].weight;
88 vex1 = LocateVex(T, E->head[i].vex1);
89 vex2 = LocateVex(T, E->head[i].vex2);
90 root1 = FindMFSet(T, vex1);
91 root2 = FindMFSet(T, vex2);
92 if (root1 != root2)/*vex1和vex2不在一棵树中*/
93 {
94 UnionMFSet(T, vex1, vex2, weight);
95 e++;
96 }
97 i++;
98 }
99 }
100 /*************************************************
101 * Name: InitMFSet
102 * Parameter: T 并查集
103 * Called By: Kruskal
104 * Description: 初始化并查集。
105 **************************************************/
106 void InitMFSet(MFSet* T)
107 {
108 int i = 0;
109 T->head = (MFSetNode*)calloc(T->vexnum, sizeof(MFSetNode));
110 for (i = 0; i < T->vexnum; i++)
111 {
112 printf("请输入第%d个顶点:", i + 1);
113 scanf(" %c", &T->head[i].data);
114 /*每个元素自成一棵树*/
115 /*令每棵树根结点的parent域存储该棵树中结点的个数*/
116 /*为避免混淆,根结点的parent域规定为负值*/
117 T->head[i].parent = -1;
118 }
119 }
120 /***************************************************
121 * Name: FindMFSet
122 * Parameter: T 并查集
123 * Called By: Kruskal, UnionMFSet, InversionMFSet.
124 * Return: 查找并返回vex所在树的根结点。
125 ****************************************************/
126 int FindMFSet(MFSet* T, int vex)
127 {
128 int i = vex;
129 while (T->head[i].parent > -1) /*找根*/
130 {
131 i = T->head[i].parent;
132 }
133 return i;
134 /*不能压缩路径,因为会破坏最小生成树的层次结构*/
135 }
136 /*********************************************************
137 * Name: UnionMFSet
138 * Parameter: T 并查集, vex1 顶点1,vex2 顶点2,
139 * weight 顶点vex1和vex2所在弧的权值
140 * Called By: Kruskal
141 * Description: 合并两棵树,并加入对应边的权值。
142 **********************************************************/
143 void UnionMFSet(MFSet* T, int vex1, int vex2, int weight)
144 {
145 int root1 = FindMFSet(T, vex1);
146 int root2 = FindMFSet(T, vex2);
147 if (T->head[root1].parent > T->head[root2].parent)/*树root1含元素比root2少, 注意根结点parent域是以负值存储的树中结点数量*/
148 {
149 if (T->head[vex1].parent > -1)
150 {
151 InversionMFSet(T, vex1);/*以当前结点为根,逆置当前结点所在树*/
152 }
153 T->head[root2].parent += T->head[vex1].parent;
154 T->head[vex1].parent = vex2;
155 T->head[vex1].weight = weight;
156 }
157 else
158 {
159 if (T->head[vex2].parent > -1)
160 {
161 InversionMFSet(T, vex2);/*以当前结点为根,逆置当前结点所在树*/
162 }
163 T->head[root1].parent += T->head[vex2].parent;
164 T->head[vex2].parent = vex1;
165 T->head[vex2].weight = weight;
166 }
167 }
168 /******************************************************
169 * Name: InversionMFSet
170 * Parameter: T 并查集, vex 顶点
171 * Called By: UnionMFSet
172 * Description: 以vex为根节点旋转vex所在的并查集树。
173 *******************************************************/
174 void InversionMFSet(MFSet* T, int vex)
175 {
176 int i = T->head[vex].parent;
177 int j = vex;
178 int weight = T->head[vex].weight;
179
180 /*设置当前结点为根结点*/
181 T->head[vex].parent = T->head[FindMFSet(T, vex)].parent;
182 /*依次设置当前结点的双亲结点指向当前结点*/
183 while (T->head[i].parent > -1)
184 {
185 i = T->head[i].parent;
186 T->head[i].parent = j;
187 j = i;
188 }
189 T->head[i].parent = j;/*原来的根结点也要补上*/
190 T->head[i].weight = T->head[j].weight;
191 }
192 /*************************************************
193 * Name: PrintMFSet
194 * Parameter: T 并查集
195 * Called By: main
196 * Description: 打印并查集树。
197 **************************************************/
198 void PrintMFSet(MFSet T)
199 {
200 int i = 0;
201 printf("最小生成树(双亲表示法):\n-----------------------------------\n");
202 printf("num\tvex\tparent\tweight\n");
203 for (i = 0; i < T.vexnum; i++)
204 {
205 printf("%d\t%c\t%3d\t%3d\n", i, T.head[i].data, T.head[i].parent, T.head[i].weight);
206 }
207 printf("-----------------------------------\n");
208 }
209 /*************************************************
210 * Name: InitArc
211 * Parameter: E 边表
212 * Called By: Kruskal
213 * Description: 初始化边表。
214 **************************************************/
215 void InitArc(Arc* E)
216 {
217 int i = 0;
218 E->head = (ArcNode*)calloc(E->arcnum, sizeof(ArcNode));
219 for (i = 0; i < E->arcnum; i++)
220 {
221 printf("请输入第%d条边的两端点和权值(逗号分隔):", i + 1);
222 scanf(" %c%*c%c%*c%d", &E->head[i].vex1, &E->head[i].vex2, &E->head[i].weight);
223 }
224 }
225 /*************************************************
226 * Name: BubbleSortArc
227 * Parameter: E 边表
228 * Called By: Kruskal
229 * Description: 冒泡排序法按升序排序边表E。
230 **************************************************/
231 void BubbleSortArc(Arc* E)
232 {
233 int i = 0;
234 int j = 0;
235 ArcNode t;
236 for (i = 1; i <= E->arcnum - 1; i++)
237 {
238 for (j = 0; j < E->arcnum - i; j++)
239 {
240 if (E->head[j].weight > E->head[j + 1].weight)
241 {
242 t = E->head[j];
243 E->head[j] = E->head[j + 1];
244 E->head[j + 1] = t;
245 }
246 }
247 }
248 }
249 /****************************************
250 * Name: LocateVex
251 * Parameter: T 并查集, vexdata 顶点
252 * Called By: Kruskal
253 * Return: 查找并返回vex在并查集中的位置;
254 * 若不存在该顶点返回-1。
255 ****************************************/
256 int LocateVex(MFSet* T, char vexdata)
257 {
258 int i = 0;
259 for (i = 0; i < T->vexnum; i++)
260 {
261 if (T->head[i].data == vexdata)
262 {
263 return i;
264 }
265 }
266 return -1;
267 }