#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define MAX 100
#define NO INT_MAX//NO表示没有边,相当于INFtypedef struct Graph
{int arcnum;int vexnum;char vextex[MAX][20];int martrix[MAX][MAX];
}Graph;
//邻接矩阵打印输出图
void print_graph(Graph* g)
{for (int i = 0; i < g->vexnum; i++){printf("\t%s", g->vextex[i]);}printf("\n");for (int i = 0; i < g->vexnum; i++){printf("%s\t", g->vextex[i]);for (int j = 0; j < g->vexnum; j++){if (g->martrix[i][j] == INT_MAX){printf("∞\t");}else{printf("%d\t", g->martrix[i][j]);}}printf("\n");}
}
//定义最小生成树的边结构
typedef struct Arc
{int begin;int end;int weight;//在边结构中加多了一个权值weight来用于判断构造最小生成树
}Arc;
//打印最小生成树
void print_tree(Graph* g, Arc* arc, int count)
{printf("最小生成树:\n");for (int i = 0; i < count; i++){printf("%s--->%s\n", g->vextex[arc[i].begin], g->vextex[arc[i].end]);}
}
//比较函数(用于在qsort中,因为qsort是一个泛函数,开始时未知函数类型,需要在调用函数时再给出),
//所以这个比较基准函数需要使用强制类型转换
int compare_arc(const void* a, const void* b)
{return(((Arc*)a)->weight-((Arc*)b)->weight);//强制转化为arc类型,根据边结构的weight值进行比较,如果大于则返回正数,等于则返回0,小于则返回负数
}
//克鲁斯卡尔算法
void graph_kruskal(Graph* g)
{//定义边结构数组arc用于保存图中存在的边Arc* arc = (Arc*)malloc(sizeof(Arc) * g->vexnum * g->vexnum);assert(arc);int count=0;//count用于存边for (int i = 0; i < g->vexnum; i++){for (int j = 0; j < g->vexnum; j++){if (g->martrix[i][j] != NO){(arc + count)->begin = i;(arc + count)->end = j;(arc + count)->weight = g->martrix[i][j];count++;}}}//快速排序根据边权值进行排序//(注意快速排序是不稳定的,所以选边的顺序不是按照原顺序的)qsort(arc,count,sizeof(Arc),compare_arc);//定义parent整型数组,大小为顶点数int* parent = (int*)malloc(sizeof(int) * g->vexnum);assert(parent);for (int i = 0; i < g->vexnum; i++){parent[i] = i;//初始化时先将每个结点的前驱结点都置为自身}Arc* result = (Arc*)malloc(sizeof(Arc) * g->arcnum);//result是用于存储对边进行排序后的边结构 assert(result);count = 0;//count用于跟踪已选的边的数量int i = 0;//i用于遍历所有的边printf("Kruskal的选边过程如下:\n");while (count < g->vexnum - 1)//当边没选够时则继续进行{int x = (arc + i)->begin;int y = (arc + i)->end;//初始化两个变量来存x和y的根节点int root_x = x;int root_y = y;while (parent[root_x] != root_x){root_x = parent[root_x];//进行压缩路径,将其根节点设为根节点的根节点}while (parent[root_y] != root_y){root_y = parent[root_y];}if (root_x != root_y)//如果不构成一个环的话{result[count] = arc[i]; //将该边加入到最小生成树中count++;printf("选择边:%s---%s,权值为:%d\n",g->vextex[x],g->vextex[y],g->martrix[x][y]);parent[root_x] = root_y;//将所选边前面点的前驱节点置为后边的点(也是起到类似压缩路径的效果,谁先谁后好像关系不大)}i++;}print_tree(g, arc, count);free(arc);free(result);free(parent);
}int main()
{Graph g = { 10,7,{"A","B","C","D","E","F","G"},{{NO,50,60,NO,NO,NO,NO},{50,NO,NO,65,40,NO,NO},{60,NO,NO,52,NO,NO,45},{NO,65,52,NO,50,30,42},{NO,40,NO,50,NO,70,NO},{NO,NO,NO,30,70,NO,NO},{NO,NO,45,42,NO,NO,NO}} };print_graph(&g);graph_kruskal(&g);return 0;
}