prim算法_C语言实现常用数据结构:最小生成树之Kruskal算法(第20篇)

4ed240710a28e41b8c8d28944007edff.gif

「今天是学习C语言第 163 天」

纸上学来终觉浅,绝知此事要躬行。——  陆游「冬夜读书示子聿」

# 最小生成树

对于一个n个顶点的连通图,可以选择n-1条边构成生成树,树中的代价等于n-1条边的和,因此最小生成树是图生成的代价最小的生成树。

求解最小生成树算法主要有两种,分别是Prim算法和Kruskal算法。下一篇学习Prim算法。

# Kruskal算法基本思想

对n个顶点构成的连通图中所有的边从小到大排序,每次选择代价最小的边,且该边不会和已选的边形成环路,直到选择n-1条边为止,即可获得最小生成树。

Kruskal算法时间复杂度是O(eloge),其中e表示边的条数,由此可见Kruskal算法适合顶点较多、边较少的稀疏图。

# 实现要点

1.图中的所有边需要从小到大排序,可以使用各种排序方法;

2.使用并查集算法检查环路,判断当前选择的边上两端顶点是否已经在一个连通分量的顶点集合中。

备注:并查集算法可以参见前一篇文章。

C语言实现常用数据结构:并查集(第19篇)

# 运行结果

求以下图的最小生成树,将最小生成树的边依次输出。

7e70295d264fc849feb65d6ba595a429.png

edge : 2 ---- 3 , cost : 1edge : 1 ---- 3 , cost : 2edge : 0 ---- 1 , cost : 3edge : 1 ---- 4 , cost : 4edge : 4 ---- 5 , cost : 5--------------------------------Process exited after 0.6048 seconds with return value 0请按任意键继续. . .

# 代码

/*========================================== 名称   :C语言实现常用数据结构 功能   :Kruskal最小生成树算法  环境   :Windows 10 + Dev-C++编译 作者   :一只会C的猫 公众号 :C语言大全(coderpointer) 时间   :2020.9.25==========================================*/#include #include #include #include struct edge {  // 边上顶点编号、代价   int src, dst, cost;};struct graph {  // 顶点、边个数   int v, e;  // 使用动态分配保持所有边的信息   struct edge *edge;}; // 创建并查集 int *new_set(int n){  int *set = (int *)malloc(n * sizeof(int));  assert(set);  // 初始化,每个顶点单独一个集合  int i;  for(i = 0; i < n; i++)     set[i] = i;  return set;}// 并查集中查找顶点x的根节点 int find_set(int set[], int x){  assert(set);  int root = x;  while(set[root] != root)    root = set[root];  // 压缩路径  set[x] = root;   return root;}// 合并集合void union_set(int set[], int x, int y){  assert(set);  int rx, ry;  rx = find_set(set, x);  ry = find_set(set, y);  if(rx != ry)    set[rx] = ry;   } // 检测是否有环int is_circle(int set[], int x, int y){  return   find_set(set, x) == find_set(set, y);} // 比较大小函数int edge_cmp(const void *x, const void *y){  struct edge *e1 = (struct edge *)x;  struct edge *e2 = (struct edge *)y;  return e1->cost - e2->cost;} void kruskal_mst(struct graph *graph){  assert(graph);  // 保持最小生成树的边,边的个数是顶点数-1   struct edge *edge_mst = (struct edge *)malloc \  ((graph->v - 1) * sizeof(struct edge));   // 图的边按照代价从小到大排序  qsort(graph->edge, graph->e, sizeof(graph->edge[0]), \  edge_cmp);         // 每次选择代价最小的边,且不形成环  // 创建并查集,用于检测环路  int *set = new_set(graph->v);  int i;  struct edge *p = graph->edge;  for(i = 0; i < graph->v - 1; i++)  {    // 如果有环,则选取下一条边     while(is_circle(set, p->src, p->dst))      p++;    edge_mst[i] = *p;    union_set(set, p->src, p->dst);    p++;  }     // 输出最小生成树的边  for(i = 0; i < graph->v - 1; i++)    printf("edge : %d ---- %d , cost : %d\n", i, edge_mst[i].src, \      edge_mst[i].dst, edge_mst[i].cost); } // 创建图数据结构 struct graph *create_graph(int v, int e){  struct graph *graph = (struct graph *)malloc( \                sizeof(struct graph));  assert(graph);  graph->v = v;  graph->e = e;    struct edge *edge = (struct edge *)malloc( \                e * sizeof(struct edge));  assert(edge);  graph->edge = edge;  return graph;}int main(void){  struct graph *graph = create_graph(6, 8);    // 顶点0和1之间的边   graph->edge[0].src = 0;  graph->edge[0].dst = 1;  graph->edge[0].cost = 3;    // 顶点0和2之间的边   graph->edge[1].src = 0;  graph->edge[1].dst = 2;  graph->edge[1].cost = 8;    // 顶点1和3之间的边   graph->edge[2].src = 1;  graph->edge[2].dst = 3;  graph->edge[2].cost = 2;    // 顶点1和4之间的边   graph->edge[3].src = 1;  graph->edge[3].dst = 4;  graph->edge[3].cost = 4;    // 顶点2和3之间的边   graph->edge[4].src = 2;  graph->edge[4].dst = 3;  graph->edge[4].cost = 1;    // 顶点2和4之间的边   graph->edge[5].src = 2;  graph->edge[5].dst = 4;  graph->edge[5].cost = 6;    // 顶点3和5之间的边   graph->edge[6].src = 3;  graph->edge[6].dst = 5;  graph->edge[6].cost = 8;    // 顶点4和5之间的边   graph->edge[7].src = 4;  graph->edge[7].dst = 5;  graph->edge[7].cost = 5;      kruskal_mst(graph);  return 0;}

---------- End ----------

往期精彩推荐:

一万分钟C语言学习计划:2020开篇

C语言内存管理的两种方式

C89标准库功能简介

C语言链接与存储类型

C语言标准输入输出

C语言入门基本语法

更多请点击公众号历史文章...

752fabf4fdaa31b864d1faab435389ad.png

「喜欢C请赏个 赞d1560815f18a1e43f6340f967fd0951f.png    点击右下角 在看」

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值