最近在复习数据结构,所以想起了之前做的一个最小生成树算法。用Kruskal算法实现的,结合堆排序可以复习回顾数据结构。现在写出来与大家分享。

  最小生成树算法思想:书上说的是在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集(即)且为无循环图,使得的 w(T) 最小,则此 T 为 G 的最小生成树。说白了其实就是在含有 n 个顶点的连通网中选择 n-1 条边,构成一棵极小连通子图,并使该连通子图中 n-1 条边上权值之和达到最小,则称最小生成树

  本程序用的是克鲁斯卡尔算法(Kruskal),也可以使用prim算法实现。Kruskal思想是在带权连通图中,不断地在排列好的边集合中找到最小的边,如果该边满足得到最小生成树的条件,就将其构造,直到最后得到一颗最小生成树。

图的顶点存储结构体:

1 //结构体定义,储存图的顶点
2 typedef struct {
3     int from; //边的起始顶点
4     int to;   //边的终止顶点
5     int cost; //边的权值
6 }Edge;

问题:顶点编号的类型。

  好的程序应该可以扩展,不论顶点用0,1,2...  顺序编号还是用5,2,1,7... 乱序编号还是用a,b,c...  英文编号都应该可以做到兼容通过,所以在存储图的节点的时候我做了一个映射,就是不论输入的什么编号一律转换成顺序编号0,1,2...,在最后输出的时候再把编号映射回原来的编号,这样就可以应对不同而顶点编号。如下面程序:

 1 for(i = 0;i < edgeNum; i++){
 2     printf("请输入第 %d 条边!\n",i+1);
 3     scanf(" %c %c %d",&from,&to,&cost);
 4     edge_temp[i][0] = judge_num(from);
 5     edge_temp[i][1] = judge_num(to);
 6     edge_temp[i][2] = cost;
 7 }
 8 //对输入的边和点信息进行堆排序
 9 HeapSort(edge_temp,edgeNum);
10 int j;
11 for(j = 0;j < edgeNum; j++){
12     edge[j].from = edge_temp[j][0];
13     edge[j].to = edge_temp[j][1];
14     edge[j].cost = edge_temp[j][2];
15 }

每次输入顶点后都会先保存到临时存储数组edge_temp中,进行堆排序后再把数据白存在真正的数据中。其中判断是否形成回路借助了一个递归方法:

1 //用于判断是否形成回路
2 int judge(int num){
3     if(num == judge_temp[num]){
4         return judge_temp[num];
5     }
6     return judge_temp[num] = judge(judge_temp[num]);
7 }

执行步骤:

1:在带权连通图中,将边的权值排序(本程序用的是堆排序);

2:判断是否需要选择这条边(此时的边已按权值从小到大排好序)。判断的依据是边的两个顶点是否已连通,如果连通则继续下一条;如果不连通,那么就选择使其连通。

3:循环第二步,直到图中所有的顶点都在同一个连通分量中,即得到最小生成树。

判断法则:(当将边加入到已找到边的集合中时,是否会形成回路?)

1:如果没有形成回路,那么直接将其连通。此时,对于边的集合又要做一次判断:这两个点是否在已找到点的集合中出现过?如果两个点都没有出现过,那么将这 两个点都加入已找到点的集合中;如果其中一个点在集合中出现过,那么将另一个没有出现过的点加入到集合中;如果这两个点都出现过,则不用加入到集合中。

2:如果形成回路,不符合要求,直接进行下一次操作。

重点类容就这么多,下面给出源程序,程序直接复制后可以运行,有兴趣的朋友也可以用prim算法实现。

  1 #include <stdio.h>
  2 #include <string.h>
  3 //常量定义,边点最大数量限制50;
  4 #define MAXE 52
  5
  6 /*
  7  * Info:最小生成树算法源码(C语言版)
  8  * @author: zhaoyafei
  9  * time: 2015
 10  */
 11
 12 //结构体定义,储存图的顶点
 13 typedef struct {
 14     int from; //边的起始顶点
 15     int to;   //边的终止顶点
 16     int cost; //边的权值
 17 }Edge;
 18
 19 int nodeNum;  //顶点数;
 20 int edgeNum;  //边数;
 21 int min_cost; //记录最小生成树(权值)
 22 int judge_temp[MAXE]; //记录判断是否成环
 23 int sort[MAXE][MAXE]; //用来做排序
 24 int edge_temp[MAXE][3]; //用于存储堆排序边点信息
 25
 26 Edge edge[MAXE];        //用于存储边点信息
 27 Edge min_edge[MAXE];    //用于存储最小生成树边点信息
 28
 29 char judge_num_int[MAXE];
 30 int inputs = 1;
 31 void HeapSort(int array[MAXE][3],int length);
 32 int judge_num(char from);
 33
 34 //save_point()函数,存储图的点边信息;
 35 void save_point(){
 36     char from;
 37     char to;
 38     int cost = 0;
 39     int i;
 40     for(i = 0;i < edgeNum; i++){
 41         printf("请输入第 %d 条边!\n",i+1);
 42         scanf(" %c %c %d",&from,&to,&cost);
 43
 44         edge_temp[i][0] = judge_num(from);
 45         edge_temp[i][1] = judge_num(to);
 46         edge_temp[i][2] = cost;
 47     }
 48     //对输入的边和点信息进行堆排序
 49     HeapSort(edge_temp,edgeNum);
 50     int j;
 51     for(j = 0;j < edgeNum; j++){
 52         edge[j].from = edge_temp[j][0];
 53         edge[j].to = edge_temp[j][1];
 54         edge[j].cost = edge_temp[j][2];
 55     }
 56 }
 57
 58 int judge_num(char str){
 59     int n1 = 0;
 60     for(int j1 = 1;j1 < edgeNum * 2; j1++){
 61         if(str == judge_num_int[j1]){
 62             n1++;
 63         }
 64     }
 65     if(n1 == 0){
 66         judge_num_int[inputs] = str;
 67         inputs++;
 68     }
 69     int return_num = 1;
 70     for(int j2 = 1;j2 < edgeNum * 2; j2++){
 71         if(str == judge_num_int[j2]){
 72             return_num = j2;
 73         }
 74     }
 75     return return_num;
 76 }
 77
 78 //用于判断是否形成回路
 79 int judge(int num){
 80     if(num == judge_temp[num]){
 81         return judge_temp[num];
 82     }
 83     return judge_temp[num] = judge(judge_temp[num]);
 84 }
 85
 86 //判断是否是一棵最小生成树
 87 bool is_judge(){
 88     int oneedge = judge(1);
 89     int i;
 90     for(i = 2;i <= nodeNum; i++)  {
 91         if(oneedge != judge(i)){
 92             return false;
 93         }
 94     }
 95     return true;
 96 }
 97
 98 //kruskal算法
 99 void kruskal(){
100     min_cost = 0;//最小生成树
101     //初始化辅助回路判断数组
102     int m;
103     for(m = 0;m < MAXE;m++)  {
104         judge_temp[m] = m;
105     }
106
107     int edge_num = 0;//记录最小生成树的边数
108     int i;
109     for(i = 0;i < edgeNum; i++){
110         //小于总节点数
111         if(edge_num != nodeNum - 1){
112             int edge_from = judge(edge[i].from);
113             int edge_to = judge(edge[i].to);
114             //如果形成回路则edge_from == edge_to;
115             if(edge_from != edge_to){
116                 //如果没有形成回路,则改变原临时数组中的值
117                 judge_temp[edge_from] = edge_to;
118                 min_cost += edge[i].cost;
119
120                 //将符合的边加入到存储数组中
121                 min_edge[edge_num].from = edge[i].from;
122                 min_edge[edge_num].to = edge[i].to;
123                 min_edge[edge_num].cost = edge[i].cost;
124
125                 edge_num++;
126             }
127         }
128     }
129 }
130
131 //array是待调整的堆数组,i是待调整的数组元素的位置,nlength是数组的长度
132 //根据数组array构建大顶堆
133 void HeapAdjust(int array[MAXE][3],int i,int nLength){
134    int nChild;
135    for(; 2*i + 1 < nLength; i = nChild){  //子结点的位置=2*(父结点位置)+1
136         nChild = 2*i + 1;
137         //得到子结点中较大的结点
138         if(nChild < nLength-1 && array[nChild+1][2] > array[nChild][2]){
139             ++nChild;
140         }
141         //如果较大的子结点大于父结点那么把较大的子结点往上移动,替换它的父结点
142         if(array[i][2] < array[nChild][2]){
143             int temp_arr2[3];
144             temp_arr2[0] = array[i][0];
145             temp_arr2[1] = array[i][1];
146             temp_arr2[2] = array[i][2];
147
148             array[i][0] = array[nChild][0];
149             array[i][1] = array[nChild][1];
150             array[i][2] = array[nChild][2];
151
152             array[nChild][0] = temp_arr2[0];
153             array[nChild][1] = temp_arr2[1];
154             array[nChild][2] = temp_arr2[2];
155         }else{
156             break;//否则退出循环
157         }
158     }
159 }
160
161 //堆排序算法
162 void HeapSort(int array[MAXE][3],int length){
163     //调整序列的前半部分元素,调整完之后第一个元素是序列的最大的元素
164     //length/2-1是最后一个非叶节点,此处"/"为整除
165     int j;
166     for( j= length/2 - 1; j >= 0; --j){
167         HeapAdjust(array,j,length);
168     }
169     //从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
170     int i;
171     for(i = length - 1; i > 0; --i){
172         //把第一个元素和当前的最后一个元素交换,
173         //保证当前的最后一个位置的元素都是在现在的这个序列之中最大的
174         int temp_arr1[3]; //构建二维数组的原因:交换后保证数组中其他属性值同时交换
175         temp_arr1[0] = array[i][0];
176         temp_arr1[1] = array[i][1];
177         temp_arr1[2] = array[i][2];
178
179         array[i][0] = array[0][0];
180         array[i][1] = array[0][1];
181         array[i][2] = array[0][2];
182
183         array[0][0] = temp_arr1[0];
184         array[0][1] = temp_arr1[1];
185         array[0][2] = temp_arr1[2];
186
187         //不断缩小调整heap的范围,每一次调整完毕保证第一个元素是当前序列的最大值
188         HeapAdjust(array,0,i);
189     }
190 }
191
192 //输出最小生成树的信息(包括边点和权值)
193 void output(){
194     if(is_judge()){
195         printf("最小生成树:\n");
196         printf("起点 -> 终点   路径长:\n");
197         for(int i = 0;i < nodeNum-1; i++){
198             printf(" %c   ->  %c       %d\n",judge_num_int[min_edge[i].from],judge_num_int[min_edge[i].to],min_edge[i].cost);
199         }
200         printf("min cost is : %d\n",min_cost);
201         printf("*******************************************************************************\n");
202         printf("请输入 节点数 边数(中间需用空格隔开):\n");
203     }else{
204         printf("最小生成树不存在!\n");
205         printf("*******************************************************************************\n");
206         printf("请输入 节点数 边数(中间需用空格隔开):\n");
207     }
208 }
209
210 /*
211  * 程序主方法;
212  * 用于开始程序,介绍程序操作须知;
213  */
214 int main(){
215     printf("*******************************************************************************\n");
216     printf("**                         最小生成树(kruskal算法)                        ***\n");
217     printf("**  说明:开始程序输入图的总点数和总边数,测试程序目前边点限制最多输入50个  ***\n");
218     printf("**        中间用空格隔开。输入边和点信息,需要按格式:开始边 终止边  权值   ***\n");
219     printf("**        本次计算结束可以按要求开始下次计算。                              ***\n");
220     printf("*******************************************************************************\n");
221     printf("请输入 节点数 边数(中间需用空格隔开):\n");
222     while(scanf("%d%d",&nodeNum,&edgeNum) != EOF){
223         //判断输入的边和点的合法性
224         if(nodeNum < 1 || edgeNum < 1){
225             printf("输入的数据不合法\n");
226             printf("请输入 节点数 边数(中间需用空格隔开):\n");
227             return 0;
228         }else if(nodeNum > 50 || edgeNum > 50){
229             printf("输入的边或者点不能大于50\n");
230             printf("请输入 节点数 边数(中间需用空格隔开):\n");
231             return 0;
232         }else{
233             printf("请输入 开始节点 终止节点 该边的权值(中间需用空格隔开,回车换行):\n");
234             printf("共 %d 条边\n",edgeNum);
235             for(int m = 0;m < MAXE; m++)  {
236                 judge_num_int[m] = '-';
237             }
238             inputs = 1;
239             save_point(); //存储边点信息
240             kruskal();    //算法执行
241             output();     //输出执行结果
242         }
243     }
244     return 0;
245 }

运行结果如下:

转载于:https://www.cnblogs.com/zyf-zhaoyafei/p/4604006.html

最小生成树的Kruskal算法实现相关推荐

  1. 最小生成树:Kruskal算法 和 Prim算法(第23章)

    武侠: 飞雪连天射白鹿,笑书神侠倚碧鸳. --金庸十四著作 飞狐外传 .雪山飞狐 .连城诀 .天龙八部 .射雕英雄传 .白马啸西风 .鹿鼎记 .笑傲江湖 .书剑恩仇录 .神雕侠侣 .侠客岛 .倚天屠龙 ...

  2. 数据结构------最小生成树之Kruskal算法

    盛年不重来,一日难再晨.及时当勉励,岁月不待人. <杂诗>陶渊明 目录 前言 一.Kruskal的几何思维 二.使用步骤 1.核心思想 2.全部测试代码 总结 前言 最小生成树算法有两种一 ...

  3. 最小生成树的Kruskal算法-详解

    最小生成树的Kruskal算法 一. 什么是最小生成树 1.1 最小生成树定义: 一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边.最 ...

  4. kruskal算法c语言,最小生成树之Kruskal算法

    上一篇文章中提到了最小生成树的Prim算法,这一节继续探讨一下最小生成树的Kruskal算法.什么是最小生成树算法上文已经交代过了,所以我们直接从Kruskal的步骤开始介绍. 1.Kruskal算法 ...

  5. ds图—最小生成树_Java: Kruskal算法生成最小生成树(邻接矩阵)

    Java: Kruskal算法生成最小生成树(邻接矩阵): package 输出: Kruskal=36: (E,F) (C,D) (D,E) (B,F) (E,G) (A,B) 分析: Java: ...

  6. python【数据结构与算法】最小生成树之Kruskal算法

    我们用现在来模拟一下Kruskal算法,下面给出一个无向图B,我们使用Kruskal来找无向图B的最小生成树. 首先,我们将所有的边都进行从小到大的排序.排序之后根据贪心准则,我们选取最小边(A,D) ...

  7. 最小生成树之Kruskal算法

    图片描述 算法思想 采用贪婪策略,连续的按最小的权选择边,并且当边不产生圈时就把它作为取定的边 算法思路 问题出现 1.怎样选择最小权的边 用个排序算法 2.怎样判断加入的边是否会产生圈 (用到不相交 ...

  8. 最小生成树学习-Kruskal算法

    转载请注明来源 最小生成树简单的来说就是从无向连通图的邻接表或者邻接矩阵中扣下来一棵权值最小的树,他只有n-1条边来连接n个顶点,并且不允许产生回路. Kruskal算法首先要对边进行排序,sort一 ...

  9. 最小生成树(Kruskal算法+Prim算法)简单讲解+最小生成树例题 acm寒假集训日记22/1/8

    算法简讲部分: Kruskal算法: 基于贪心策略大致过程分为第三步:1. 我们先用结构体把每条边的端点和权值记录下来,然后对每条边按权值进行排序2. 因为 使图连通最少需要n-1 条边,所以我们依次 ...

最新文章

  1. mybatis delete返回值_从零开始学习在IntelliJ IDEA 中使用mybatis
  2. websocket 工作原理
  3. asp.net发布网站的详细步骤
  4. mysql for 语句执行顺序_MySQL使用profile分析SQL语句执行过程
  5. TypeScript极速完全进阶指南-2中级篇
  6. 模式设计趣解——追MM篇
  7. Python 正则式学习笔记 [转]
  8. centos7使用kubeadm部署k8s集群(使用containerd做运行时)
  9. 内存条hyperx_一键开启内存条最高效能 HyperX雷电系列3733MHz内存条评测
  10. 【仿真】Carla介绍与使用 [1] (附代码手把手讲解)
  11. 从物理到软件工程,中山大学转专业2017纪实
  12. 第三章 分类模型-随机森林知识点详细总结
  13. Matlab是常见的高级语,高级语言具有哪些特点 试述低级语言与高级语言的特点...
  14. 风控每日一问:风控工作的价值在于?
  15. ESP8266学习笔记(3)——GPIO接口使用
  16. thinkpad E450/550 预装系统改装WIN7全套教程
  17. Docker修改无法启动的容器的配置文件
  18. FileNotFoundException(/storage/emulated/0/DCIM/Camera/xx.jpg: open failed: EACCES (Permission denied
  19. 【实用代码】选项卡切换——带标题底纹样式
  20. 悄悄说--一个Swing界面的仿qq聊天软件

热门文章

  1. JZOJ 5274. 数组
  2. linux nfs时间不对,NFS挂载主机或不稳定的原因与解决方法
  3. ubuntu怎么打中文_记录一下我在笔记本上安装ubuntu+win10系统的过程,仅供参考
  4. iphone导出通讯录到安卓_科技资讯:iPhone苹果手机换新机如何将旧手机的通讯录导出到新的...
  5. 2022.2.21显示器连接器引脚信号定义1
  6. CVPR 2017 《Deep Feature Flow for Video Recognition》论文笔记
  7. mysql行级安全_MySQL学习笔记(五):MySQL表级锁和行级锁
  8. html 正则表达式 中文,正则表达式的中文搜索
  9. CentOS7安装配置redis5.0.5
  10. Django内置的分页模块