数据结构课程设计-克鲁斯卡尔算法最小生成树

假设连通网N=(V,{E}),则令最小生成树的初始状态为只有n个顶点而无边的非连通图T=(V,{∮}),图中每个顶点自成一个连通分量。在E中选择代价最小的边,若该边依附的顶点落在T中不同的连通分量上,则将此边加入到T中,否则舍去此边而选择下一条代价最小的边。依次类推,直至T中所有顶点都在同一连通分量上为止。

(1)根据原图,构造一个只含n个顶点,边集为空的子图。若将图中各个顶点看成一棵树的根节点,则它是一个含有n棵树的森林。

(2)从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图。也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之

(3)依次类推,直至森林中只有一棵树,也即子图中含有 n-1条边为止。

手动输入建立邻接矩阵:

首先输入城市数目n,建立n*n大小空矩阵。其次输入城市名称,并利用map函数使名称与数字可以相互转化。最后输入可构建线路的条数x,并输入x条边及权值填充邻接矩阵。

寻找根结点

根节点数组中保存对应的上一节点的值。依次往上寻找,知道数组中存储值为0,此时的参数即位目前的根节点。

克鲁斯卡尔算法输出

建立边集数组,保证起始点的数值小于终点。利用sort函数,按权值将边从小到大排列。从小到大遍历所有边,查找每条边的起始点的根节点和终点的根节点。如果不同,证明加上这条边不会产生回路,输出这条边的信息,并使节点数组中终点的上一节点为初始点。

界面:

代码如下:

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <stdlib.h>
  5 #include <algorithm>
  6 #include <map>
  7 #include <iomanip>
  8 #include <fstream>
  9 using namespace std;
 10
 11 #define max_city 100   //最大城市数
 12
 13 map <string,int> city_numbers;  //城市->编号
 14
 15 map <int,string> numbers_city;  //编号->城市
 16
 17 const int inf=0x3f3f3f3f;
 18
 19 typedef struct
 20 {
 21     int maps[max_city][max_city];   //邻接矩阵
 22     int city_number;
 23     int city_way;  //城市数,线路数
 24 } Graph ;
 25
 26 typedef struct
 27 {
 28     int way_begin;
 29     int way_end;
 30     int way_weight;
 31 } Ways;
 32 //线路集
 33
 34 void BuildGraph1(Graph *this_graph)
 35 {
 36     printf("请输入城市数(按回车键结束输入):\n");
 37     cin>>this_graph->city_number;
 38
 39     for (int i=0;i<this_graph->city_number;i++)
 40     {
 41         for(int j=0;j<this_graph->city_number;j++)
 42         {
 43             if (i==j)
 44                 this_graph->maps[i][j]=0;
 45             else
 46                 this_graph->maps[i][j]=this_graph->maps[j][i]=inf;
 47         }
 48     }
 49
 50     printf("请输入城市名称(城市名字之间用空格隔开,按回车键结束输入):\n");
 51     for(int i=0;i<this_graph->city_number;i++)
 52     {
 53         char name[1000];
 54         cin>>name;
 55         city_numbers[name]=i;
 56         numbers_city[i]=name;
 57     }
 58
 59     printf("请输入可构架线路数(按回车键结束输入):\n");
 60     cin>>this_graph->city_way;
 61
 62     printf("请输入每条线路两端的城市名及权值:\n");
 63     for(int i=0;i<this_graph->city_way;i++)
 64     {
 65         char start[1000],last[1000];
 66         int weight;
 67         printf("请输入第%d条线路(城市名及权值用空格隔开,每条线路按回程键结束输入):\n",i+1);
 68
 69         cin>>start>>last>>weight;
 70         this_graph->maps[city_numbers[start]][city_numbers[last]]=this_graph->maps[city_numbers[last]][city_numbers[start]]=weight;
 71     }
 72
 73 }
 74 //手动输入信息
 75
 76 bool BuildGraph2(Graph *this_graph)
 77 {
 78     FILE *fp;
 79     char address[1000];
 80     printf("请输入文件的地址(按回车键结束输入):\n");
 81     cin>>address;
 82
 83     if((fp=fopen(address,"r"))==NULL)
 84         return false;
 85
 86     if(fscanf(fp,"%d",&this_graph->city_number)==EOF)
 87         return false;
 88
 89     for (int i=0;i<this_graph->city_number;i++)
 90     {
 91         for(int j=0;j<this_graph->city_number;j++)
 92         {
 93             if (i==j)
 94                 this_graph->maps[i][j]=0;
 95             else
 96                 this_graph->maps[i][j]=this_graph->maps[j][i]=inf;
 97         }
 98     }
 99
100     for(int i=0;i<this_graph->city_number;i++)
101     {
102         char name[1000];
103         if(fscanf(fp,"%s",name)==EOF)
104             return false ;
105         city_numbers[name]=i;
106         numbers_city[i]=name;
107     }
108
109     if(fscanf(fp,"%d",&this_graph->city_way)==EOF)
110         return false;
111
112     for(int i=0;i<this_graph->city_way;i++)
113     {
114         char start[1000],last[1000];
115         int weight;
116         if(fscanf(fp,"%s%s%d",start,last,&weight)==EOF)
117              return false;
118         this_graph->maps[city_numbers[start]][city_numbers[last]]=this_graph->maps[city_numbers[last]][city_numbers[start]]=weight;
119     }
120
121     fclose(fp);
122     return true;
123 }
124 //从文件读入信息
125
126 bool cmp(Ways first,Ways last)
127 {
128     return first.way_weight<last.way_weight;
129 }
130  //排序条件
131
132 int find_parent(int parent[],int find_city)
133 {
134     while(parent[find_city]>0)
135     {
136         find_city=parent[find_city];
137     }
138     return find_city;
139 }
140 //寻找根节点
141
142 void Putout1(Graph this_graph)
143 {
144     int num=0;
145     int parent[max_city];
146     Ways way[max_city];
147     memset(parent,0,sizeof(parent));
148
149     for (int i=0;i<this_graph.city_number;i++)
150     {
151         for(int j=i+1;j<this_graph.city_number;j++)
152         {
153             if(this_graph.maps[i][j]!=inf)
154             {
155                 way[num].way_begin=i;
156                 way[num].way_end=j;
157                 way[num++].way_weight=this_graph.maps[i][j];
158             }
159         }
160     }
161     //建立边集
162
163     sort(way,way+num,cmp);
164     //边集排序
165
166     int sum=0;
167     printf("最低经济方案如下如下:\n");
168     cout<<"编号"<<setw(8)<<"城市1"<<setw(8)<<"城市2"<<setw(8)<<"权值"<<endl;
169     for (int i=0;i<this_graph.city_way;i++)
170     {
171         int n=find_parent(parent,way[i].way_begin);
172         int m=find_parent(parent,way[i].way_end);
173         //寻找首位两点的根
174
175         if (n != m)
176         {
177             parent[n] = m;
178             char city1[1000],city2[1000];
179             cout<<++sum<<setw(8)<<numbers_city[way[i].way_begin]<<setw(8)<<numbers_city[way[i].way_end]<<setw(8)<<way[i].way_weight<<endl;
180         }
181         //假如n与m不等,说明两个顶点不会产生回路
182     }
183 }
184 //直接输出结果
185
186 bool Putout2(Graph this_graph)
187 {
188     int num=0;
189     int parent[max_city];
190     Ways way[max_city];
191     memset(parent,0,sizeof(parent));
192
193     for (int i=0;i<this_graph.city_number;i++)
194     {
195         for(int j=i+1;j<this_graph.city_number;j++)
196         {
197             if(this_graph.maps[i][j]!=inf)
198             {
199                 way[num].way_begin=i;
200                 way[num].way_end=j;
201                 way[num++].way_weight=this_graph.maps[i][j];
202             }
203         }
204     }
205     //建立边集
206
207     sort(way,way+num,cmp);
208     //边集排序
209
210     fstream fp;
211     printf("请输入文件的地址(按回车键结束输入):\n");
212     int sum=0;
213     char address[1000];
214     cin>>address;
215     fp.open(address);
216
217     if(!fp)
218         return false;
219     fp<<"最低经济方案如下如下:"<<endl;
220     fp<<"编号"<<setw(8)<<"城市1"<<setw(8)<<"城市2"<<setw(8)<<"权值"<<endl;
221     for (int i=0;i<this_graph.city_way;i++)
222     {
223         int n=find_parent(parent,way[i].way_begin);
224         int m=find_parent(parent,way[i].way_end);
225         //寻找首位两点的根
226
227         if (n != m)
228         {
229             parent[n] = m;
230             char city1[1000],city2[1000];
231             fp<<++sum<<setw(8)<<numbers_city[way[i].way_begin]<<setw(8)<<numbers_city[way[i].way_end]<<setw(8)<<way[i].way_weight<<endl;
232         }
233     }
234     //假如n与m不等,说明两个顶点不会产生回路
235     return true;
236 }
237 //打印到文件中
238
239 int main()
240 {
241     int  ch;
242
243     while(true)
244     {
245
246         printf("|-------------------------------------------------------|\n");
247         printf("|\t\t\t\t\t\t\t|\n");
248         printf("|\t\t  欢迎进入最小生成树系统!  \t\t|\n");
249         printf("|\t\t\t\t\t\t\t|\n");
250         printf("|-------------------------------------------------------|\n");
251         printf("|\t\t\t\t\t\t\t|\n");
252         printf("|\t\t    1——手动输入信息          \t\t|\n");
253         printf("|\t\t    2——从文件读入信息        \t\t|\n");
254         printf("|\t\t    0——退出系统        \t\t|\n");
255         printf("|\t\t\t\t\t\t\t|\n");
256         printf("|-------------------------------------------------------|\n");
257         printf("请输入选项编号(0 ~ 2): (按回车键结束输入)\n");
258
259         Graph this_graph;
260         cin>>ch;
261
262         if(ch==0)
263             break;
264
265         if(ch==1)
266             BuildGraph1(&this_graph);
267
268         else if(ch==2)
269         {
270             if(!BuildGraph2(&this_graph))
271             {
272                 printf("打开文件读入信息失败!\n");
273                 continue;
274             }
275             printf("打开文件读入信息成功!\n");
276         }
277
278         printf("|-------------------------------------------------------|\n");
279         printf("|\t\t\t\t\t\t\t|\n");
280         printf("|\t\t    1——直接输出结果          \t\t|\n");
281         printf("|\t\t    2——输出到文件        \t\t|\n");
282         printf("|\t\t    0——退出系统        \t\t|\n");
283         printf("|\t\t\t\t\t\t\t|\n");
284         printf("|-------------------------------------------------------|\n");
285         printf("请输入选项编号(0 ~ 2): (按回车键结束输入)\n");
286
287         cin>>ch;
288         if(ch==0)
289             break;
290
291         if(ch==1)
292             Putout1(this_graph);
293
294         else if(ch==2)
295         {
296             if(!Putout2(this_graph))
297             {
298                 printf("打开文件输入信息失败!\n");
299                 continue;
300             }
301             printf("打开文件输入信息成功!\n");
302         }
303     }
304 }

 

 

时间: 2024-10-13 19:11:38

数据结构课程设计-克鲁斯卡尔算法最小生成树的相关文章

克鲁斯卡尔算法 有大坑 hdu 1102

Constructing Roads Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 15475    Accepted Submission(s): 5907 Problem Description There are N villages, which are numbered from 1 to N, and you should

最小生成树( 克鲁斯卡尔算法)

/* Name: Copyright: Author: Date: 01-12-14 20:17 Description: 最小生成树( 克鲁斯卡尔算法) 关于并查集的算法,参见<一种简单而有趣的数据结构--并查集>http://blog.csdn.net/qiaoruozhuo/article/details/39674991 */ #include<stdio.h> #include<stdlib.h> #define MAXN 1000 //最大顶点数量 #def

(转)最小生成树之普利姆算法、克鲁斯卡尔算法

 最小生成树之prim算法 边赋以权值的图称为网或带权图,带权图的生成树也是带权的,生成树T各边的权值总和称为该树的权. 最小生成树(MST):权值最小的生成树. 生成树和最小生成树的应用:要连通n个城市需要n-1条边线路.可以把边上的权值解释为线路的造价.则最小生成树表示使其造价最小的生成树. 构造网的最小生成树必须解决下面两个问题: 1.尽可能选取权值小的边,但不能构成回路: 2.选取n-1条恰当的边以连通n个顶点: MST性质:假设G=(V,E)是一个连通网,U是顶点V的一个非空子集.若(

45. 蛤蟆的数据结构笔记之四十五克鲁斯卡尔算法

本篇名言:"假如生活欺骗了你 ,不要忧郁 , 也不要愤慨 !不顺心的时候暂且容忍 : 相信吧 , 快乐的日子就会到来.--普希金" 上两篇学习了弗洛伊德和迪杰特斯拉算法.这次来看下克鲁斯卡尔算法. 欢迎转载,转载请标明出处:http://write.blog.csdn.net/postedit/47071539 1.  克鲁斯卡尔算法 克鲁斯卡尔(Kruskal)算法是在剩下的所有未选取的边中,找最小边,如果和已选取的边构成回路,则放弃,选取次小边.是实现图的最小生成树最常用的算法.

最小生成树(普利姆算法、克鲁斯卡尔算法)

给定一个加权无向连通图,如何选择一个生成树,使权利的最小总和的边缘所有树,叫最小生成树. 求最小生成树算法 (1) 克鲁斯卡尔算法 图的存贮结构採用边集数组,且权值相等的边在数组中排列次序能够是随意的.该方法对于边相对照较多的不是非常有用,浪费时间. (2) p=1313">普里姆算法 图的存贮结构採用邻接矩阵.此方法是按各个顶点连通的步骤进行,须要用一个顶点集合,開始为空集,以后将以连通的顶点陆续增加到集合中,所有顶点增加集合后就得到所需的最小生成树 . 以下来详细讲下: 克鲁斯卡尔算法

贪心算法(Greedy Algorithm)之最小生成树 克鲁斯卡尔算法(Kruskal&amp;#39;s algorithm)

克鲁斯卡尔算法(Kruskal's algorithm)是两个经典的最小生成树算法的较为简单理解的一个.这里面充分体现了贪心算法的精髓.大致的流程能够用一个图来表示.这里的图的选择借用了Wikipedia上的那个.很清晰且直观. 首先第一步,我们有一张图,有若干点和边 例如以下图所看到的: 第一步我们要做的事情就是将全部的边的长度排序,用排序的结果作为我们选择边的根据.这里再次体现了贪心算法的思想.资源排序,对局部最优的资源进行选择. 排序完毕后,我们领先选择了边AD. 这样我们的图就变成了 第

数据结构(五) 普利姆与克鲁斯卡尔的最小生成树(Swift面向对象版)

上篇博客我们聊了图的物理存储结构邻接矩阵和邻接链表,然后在此基础上给出了图的深度优先搜索和广度优先搜索.本篇博客就在上一篇博客的基础上进行延伸,也是关于图的.今天博客中主要介绍两种算法,都是关于最小生成树的,一种是Prim算法,另一个是Kruskal算法.这两种算法是很经典的,也是图中比较重要的算法了. 今天博客会先聊一聊Prim算法是如何生成最小生成树的,然后给出具体步骤的示例图,最后给出具体的代码实现,并进行测试.当然Kruskal算法也是会给出具体的示例图,然后给出具体的代码和测试用例.当

hdu 1233(还是畅通工程)(prime算法,克鲁斯卡尔算法)(并查集,最小生成树)

还是畅通工程 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 26860    Accepted Submission(s): 11985 Problem Description 某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离.省政府"畅通工程"的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路

hdu 1233(还是畅通project)(prime算法,克鲁斯卡尔算法)(并查集,最小生成树)

还是畅通project Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 26860    Accepted Submission(s): 11985 Problem Description 某省调查乡村交通状况,得到的统计表中列出了随意两村庄间的距离.省政府"畅通project"的目标是使全省不论什么两个村庄间都能够实现公路交