

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 }
浙公网安备 33010602011771号