Kruskal算法来构造最小生成树,我总结了分为以下步骤:
(1)建图,构造Kruskal边集,边集元素应该包括该边的起始顶点、终止顶点、权值;
(2)将边集按权值从小到大的顺序进行排序;
(3)从小到大依次从Kruskal边集中取边加入最小生成树集合,判断条件:将该边加入最小生成树集合,与生成树集合中原有的边不构成环;
(4)最小生成树集合中元素(构成生成树的边)的个数为原图顶点数-1时,代表最小生成树构造完毕。
Kruskal核心伪代码如下:
Kruskal(MGragh *Gra) { 对Kruskal边集按权值从小到大排序 for (边集) { // 判断边集中的边能否加入最小生成树集合 n=该边的起始顶点所能到达的最新顶点 m=该边的终止顶点所能到达的最新顶点 如果n等于m 说明该边能够成环路,所以不能加入最小生成树集合 如果n不等于m 说明该边可以加入最小生成树集合 { 更新:n能够到达m } } }
实例:
源代码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 #define MAX_VERTEX_NUM 100 6 #define MAX_EDGE_NUM 200 7 #define MAX_VERTEX_NAMELEN 100 8 9 typedef struct{ 10 char name[MAX_VERTEX_NAMELEN]; 11 }VerType; 12 13 // 图的邻接矩阵存储结构 14 typedef struct{ 15 int VertexNum,EdgeNum; // 顶点数,边数 16 VerType Vertex[MAX_VERTEX_NUM]; // 顶点集 17 int Edge[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; // 边集 18 }MGragh; 19 20 // 克鲁斯卡尔边集 21 typedef struct{ 22 int b; // 起始顶点 23 int e; // 终结顶点 24 int w; // 权值 25 }KruskalEdge; 26 KruskalEdge edge[MAX_EDGE_NUM]; 27 28 // 快速排序cmp 29 int cmp(const void *a, const void *b) 30 { 31 KruskalEdge *c = (KruskalEdge *)a; 32 KruskalEdge *d = (KruskalEdge *)b; 33 34 // 权值从小到大排序 权值相同则按起始顶点从小到大排序 35 if (c->w != d->w){ 36 return c->w - d->w; 37 } 38 else{ 39 return c->b - d->b; 40 } 41 } 42 43 // 邻接矩阵建图及建立克鲁斯卡尔边集 44 void CreateMGragh(MGragh *Gra) 45 { 46 int i,j,k,w; 47 char v1[MAX_VERTEX_NAMELEN],v2[MAX_VERTEX_NAMELEN]; 48 49 printf("请输入顶点数及边数(顶点数 边数) "); 50 scanf("%d %d%*c",&(Gra->VertexNum),&(Gra->EdgeNum)); 51 52 printf("请输入顶点信息 "); 53 for (i=0; i<Gra->VertexNum; i++){ 54 printf("%d.",i+1); 55 gets(Gra->Vertex[i].name); 56 } 57 58 // 初始化邻接矩阵 59 for (i=0; i<Gra->VertexNum; i++){ 60 for (j=0; j<Gra->VertexNum; j++){ 61 Gra->Edge[i][j] = 0; 62 } 63 } 64 printf("请输入边信息(顶点,顶点,权值) "); 65 for (i=0; i<Gra->EdgeNum; i++){ 66 printf("%d.",i+1); 67 scanf("%[^,]%*c%[^,]%*c%d%*c",v1,v2,&w); 68 69 for (j=0; j<Gra->VertexNum; j++){ 70 for (k=0; k<Gra->VertexNum; k++){ 71 if (strcmp(Gra->Vertex[j].name,v1) == 0 && strcmp(Gra->Vertex[k].name,v2) == 0){ 72 Gra->Edge[j][k] = Gra->Edge[k][j] = w; 73 74 // 构造克鲁斯卡尔边集 使起始顶点<终止顶点 75 if (j<k){ 76 edge[i].b = j; 77 edge[i].e = k; 78 edge[i].w = w; 79 } 80 else{ 81 edge[i].b = k; 82 edge[i].e = j; 83 edge[i].w = w; 84 } 85 } 86 } 87 } 88 } 89 90 // 将克鲁斯卡尔边集按权值从小到大排序 91 qsort(edge,Gra->EdgeNum,sizeof(edge[0]),cmp); 92 } 93 94 // 找到顶点t所能到达的在时间顺序上最新的点 95 // 例如p[0]=6 代表第0个点能够到达第6个点 96 int FindLastArrived(int *p, int t) 97 { 98 while (p[t] > 0){ 99 t = p[t]; 100 } 101 return t; 102 } 103 104 // 克鲁斯卡尔算法构造最小生成树 105 void MiniTreeByKruskal(MGragh *Gra) 106 { 107 int i,n,m; 108 int p[MAX_VERTEX_NUM]; 109 110 // 初始化辅助数组 111 for (i=0; i<Gra->VertexNum; i++){ 112 p[i] = 0; 113 } 114 115 printf(" Kruskal算法构造最小生成树为: "); 116 for (i=0; i<Gra->EdgeNum; i++){ 117 n = FindLastArrived(p,edge[i].b); 118 m = FindLastArrived(p,edge[i].e); 119 if (n != m){ // 如果n==m 说明存在环路 120 p[n] = m; // 将边加入生成树,并令n能到达m 121 printf("(%s --> %s) %d ",Gra->Vertex[edge[i].b].name,Gra->Vertex[edge[i].e].name,edge[i].w); 122 } 123 } 124 } 125 126 int main() 127 { 128 MGragh g; 129 CreateMGragh(&g); 130 MiniTreeByKruskal(&g); 131 return 0; 132 }
测试用例及结果: