Kruskal算法构造最小生成树

  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 }
posted @ 2022-01-26 10:01  吕辉  阅读(56)  评论(0)    收藏  举报