克鲁斯卡尔算法实验报告
求最小生成树(Kruskal算法)实验报告
求最小生成树(Kruskal算法)实验报告一、实验目的通过本次实验,掌握Kruskal算法的基本原理,能够使用该算法求解最小生成树问题,并能够进行实际应用。
同时,为学习算法的设计和分析打下基础。
二、实验内容1. 理解Kruskal算法的基本原理。
2. 实现Kruskal算法,并将其应用于求解最小生成树问题。
3. 设计实验测试用例,验证程序正确性并进行性能分析。
三、实验原理Kruskal算法是最小生成树问题的一种解决方法。
该算法基于贪心策略,通过不断选择最短的边来构造最小生成树。
实现步骤如下:1. 将所有边按权重从小到大进行排序。
2. 遍历所有边,每次选择一条没有出现在生成树中的最短边,并将该边所连接的两个顶点合并到同一连通分量中。
3. 直到所有的边都被遍历过,即可得到最小生成树。
四、实验设计本次实验的主要任务是实现Kruskal算法,并运用到最小生成树问题中。
为了测试算法的正确性和性能,需要设计适当的测试用例。
具体的实验步骤如下:1. 设计数据结构在Kruskal算法中,需要维护边的信息,并对边进行排序,同时需要维护顶点的信息。
为方便实现,可以使用C++语言的STL库中的vector和set数据结构。
vector用于存储顶点信息,set用于存储排序后的边信息。
其中,顶点包含顶点编号和连通分量编号,边包含起点、终点和边权重。
为了方便生成测试数据,定义两个常量:MAX_VERTEX和MAX_EDGE。
MAX_VERTEX表示最大顶点数量,MAX_EDGE表示最大边数量。
2. 生成测试数据为了测试算法的正确性和性能,需要生成不同大小的测试数据。
可以随机生成若干个顶点和相应的边,其中顶点编号从1开始连续编号,边的起点和终点使用随机数生成,边的权重也使用随机数生成。
3. 实现Kruskal算法根据算法原理,可以实现基本的Kruskal算法。
具体实现过程如下:1. 首先将所有的边按照权重从小到大排序,并分别初始化每个顶点的连通分量编号。
Kruskal克鲁斯卡尔最小生成树算法(贪心算法)
Kruskal克鲁斯卡尔最⼩⽣成树算法(贪⼼算法)1. /*2. * Introduction to Algorithms3. * Chapter 23 --- MST.Kruskal4. * Tanky Woo @ 5. * 2012.1.76. */7.8. #include <</span>iostream>9. #include <</span>algorithm>10. using namespace std;11.12. const int maxint = 999999;13.14. typedef struct Road{15. int c1, c2;// a到b16. int value;//权值17. }Road;18.19. int no;20. int line;//记录实际关系数21. Road road[100];//设⼀个⽐较⼤的值,实际看输⼊最⼩⽣成树中:边数e=n-122. int node[101];//最⼩⽣成树:n顶点数23.24. bool myCmp(const Road &a,const Road &b)25. {26. if(a.value <</span> b.value)27. return 1;28. return 0;29. }30.31. //node[2]=1 node[8]=1 ,node[3]=1,共同的祖先,如果(3,8)加进去,则构成回路,不要32. //有点像并查集33. int Find_Set(int n)34. {35. if(node[n]==-1)36. return n;37. return node[n]= Find_Set(node[n]);38. }39.40. bool Merge(int s1,int s2)41. {42. int r1 = Find_Set(s1);43. int r2 = Find_Set(s2);44. if(r1 == r2)//如果相等证明构成回路,则直接返回⼀个0,不要把顶点加进来(下⼀步是加进去的)45. return 0;46. if(r1 <</span> r2)47. node[r2]= r1;48. else49. node[r1]= r2;50. return 1;51. }52.53. int main()54. {55. freopen("input.txt","r", stdin);56. //初始化全为-157. memset(node,-1, sizeof(node));58. scanf("%d",&no);59. scanf("%d",&line);60. int i;61. for(i=0; i<</span>line; ++i)62. {63. cin >> road[i].c1 >> road[i].c2 >> road[i].value;64. }65. sort(road, road+line, myCmp);66. int sum = 0, count = 0;// sum是MST的值,count是记录已使⽤的点数67. for(i=0; i<</span>line; ++i)68. {69. if(Merge(road[i].c1, road[i].c2))//如果返回的为0,则证明构成回路,不要加进70. {71. count ++;72. sum += road[i].value;73. }74. if(count == no-1)//e=n-1已经连通,可以退出75. break;76. }77. cout <</span><</span> sum <</span><</span> endl;78. return 0;79. }80.81.82. /*83. input.txt:84. 985. 1486. 1 2 487. 1 8 888. 2 3 889. 2 8 1190. 3 4 791. 3 6 492. 3 9 293. 4 5 994. 4 6 1495. 5 6 1096. 6 7 297. 7 8 198. 7 9 699. 8 9 7100. */。
最小生成树算法实验报告
最小生成树算法实验报告【实验报告】最小生成树算法实验一、实验目的本次实验旨在研究最小生成树算法,通过对比不同的算法,并对实验结果进行分析,探索最小生成树算法的优劣势和适应场景。
二、实验过程1.算法介绍本次实验中我们将使用两种最小生成树算法:普里姆算法和克鲁斯卡尔算法。
- 普里姆算法(Prim算法):从一个顶点开始,不断在剩下的顶点中选择到当前已有的最小生成树的距离最小的边,将该边的另一个顶点加入树中,直到所有的顶点都加入树中。
- 克鲁斯卡尔算法(Kruskal算法):首先将所有边按照权值从小到大进行排序,然后以最小权值的边开始,依次选择权值最小且不会形成环路的边,直到找到n-1条边为止,其中n为顶点数。
2.实验步骤首先,我们使用Python语言实现了普里姆算法和克鲁斯卡尔算法。
然后,我们构造了一些测试用例,包括不同规模的图和不同权值分布的图。
最后,我们对实验结果进行对比分析。
三、实验结果1.测试用例设计我们设计了三个测试用例,分别为小规模图、中规模图和大规模图,具体如下:-小规模图:顶点数为5的图,权值随机分布。
-中规模图:顶点数为50的图,权值随机分布。
-大规模图:顶点数为100的图,权值随机分布。
2.实验结果分析我们的实验结果如下表所示:算法,小规模图,中规模图,大规模图:-------:,:------:,:------:,:------:普里姆算法,13,455,703从实验结果可以看出,对于小规模图和中规模图,普里姆算法的运行时间明显低于克鲁斯卡尔算法。
但是对于大规模图,克鲁斯卡尔算法的运行时间与普里姆算法的运行时间差距不大,甚至略小于普里姆算法。
这是因为克鲁斯卡尔算法中排序边的时间复杂度为O(ElogE),而普里姆算法中筛选最小距离的边的时间复杂度为O(V^2)。
综上所述,普里姆算法适用于较小规模的图,而克鲁斯卡尔算法适用于较大规模的图。
四、实验总结本次实验研究了最小生成树算法,通过对比实验结果,我们发现不同算法在不同规模的图上的表现有所差异。
2021年克鲁斯卡尔算法实验报告
实验报告试验原理:Kruskal 算法是一个根据图中边权值递增次序结构最小生成树方法。
其基础思想是: 设无向连通网为G=(V, E), 令G 最小生成树为T, 其初态为T=(V, {}), 即开始时, 最小生成树T 由图G 中n 个顶点组成, 顶点之间没有一条边, 这么T 中各顶点各自组成一个连通分量。
然后, 根据边权值由小到大次序, 考察G 边集E 中各条边。
若被考察边两个顶点属于T 两个不一样连通分量, 则将此边作为最小生成树边加入到T 中, 同时把两个连通分量连接为一个连通分量; 若被考察边两个顶点属于同一个连通分量, 则舍去此边, 以免造成回路, 如此下去, 当T 中连通分量个数为1 时, 此连通分量便为G 一棵最小生成树。
如教材153页图4.21(a)所表示, 根据Kruskal 方法结构最小生成树过程如图4.21 所表示。
在结构过程中, 根据网中边权值由小到大次序, 不停选择目前未被选择边集中权值最小边。
依据生成树概念, n 个结点生成树, 有n-1 条边, 故反复上述过程, 直到选择了n-1 条边为止, 就组成了一棵最小生成树。
试验目:本试验经过实现最小生成树算法, 使学生了解图数据结构存放表示, 并能了解最小生成树Kruskal 算法。
经过练习, 加强对算法了解, 提升编程能力。
试验内容:(1)假定每对顶点表示图一条边, 每条边对应一个权值;(2)输入每条边顶点和权值;(3)输入每条边后, 计算出最小生成树;(4)打印最小生成树边顶点及权值。
试验器材(设备、元器件):PC机一台, 装有C语言集成开发环境。
数据结构与程序:#include <iostream>#include <cstdio>#include <algorithm>using namespace std;#define X 105typedef struct Edge{int w;int x, y;} Edge; //储存边struct, 并储存边两端结点class GraphNode{public:int data;int father;int child;} GraphNode[X]; //储存点信息并查集类(点值, 父结点, 子结点)Edge edge[X*X];bool comp(const Edge, const Edge);void update(int);int main(){int node_num;int sum_weight = 0;FILE *in = fopen("C:\\Users\\瑞奇\\Desktop\\编程试验\\数据结构试验\\FileTemp\\in.txt", "r");cout << "Reading data from file..." << endl << endl;//cout << "Please input the total amount of nodes in this Graph: ";//cin >> node_num;fscanf(in, "%d", &node_num);//cout << "Please input the data of each node: " << endl;for(int i = 1;i <= node_num;i++){//cin >> GraphNode[i].data;fscanf(in, "%d", &GraphNode[i].data);GraphNode[i].father = GraphNode[i].child = i;} //初始化点集//cout << "Please input the relation between nodes in this format and end with (0 0 0):" << endl << "(first_node second_node egde_weight)" << endl;int x, y, w, tmp_cnt = 1;//while(cin >> x >> y >> w && w)while(fscanf(in, "%d%d%d", &x, &y, &w) != EOF && w)edge[tmp_cnt].w = w, edge[tmp_cnt].x = x, edge[tmp_cnt++].y = y;fclose(in);sort(edge+1, edge+tmp_cnt, comp); //对边权进行排序cout << "The MinSpanTree contains following edges: " << endl << endl;for(int i = 1;i <= tmp_cnt;i++) //循环找最小边if(GraphNode[edge[i].x].father != GraphNode[edge[i].y].father){int n = edge[i].x;int m = n;if(GraphNode[m].father != m) //使用并查集对边是否可用进行判定{m = GraphNode[m].father;GraphNode[m].father = GraphNode[edge[i].y].father;}GraphNode[edge[i].x].father = GraphNode[edge[i].y].father;GraphNode[edge[i].y].child = GraphNode[edge[i].x].child;while(GraphNode[n].child != n)n = GraphNode[n].child;update(n); //在合并点集后对并查集进行更新sum_weight += edge[i].w; //计算总权cout << "\t" << "The edge between " << GraphNode[edge[i].x].data << " & " << GraphNode[edge[i].y].data << " with the weight " << edge[i].w << endl;}cout << endl << "And the total weight of the MinSpanTree add up to: " << sum_weight << endl;return 0;}bool comp(const Edge a, const Edge b){return a.w < b.w;}void update(int n){if(GraphNode[n].father == n)return;GraphNode[GraphNode[n].father].child = GraphNode[n].child;//更新孩子结点update(GraphNode[n].father); //递归更新GraphNode[n].father = GraphNode[GraphNode[n].father].father;//更新父结点}程序运行结果:运行程序, 程序读取文件, 获取文件中相关图信息: 结点数, 结点值, 结点间边权。
数据结构-kruskal算法求最小生成树 实验报告
一、问题简述题目:图的操作。
要求:用kruskal算法求最小生成树。
最短路径:①输入任意源点,求到其余顶点的最短路径。
②输入任意对顶点,求这两点之间的最短路径和所有路径。
二、程序设计思想首先要确定图的存储形式。
经过的题目要求的初步分析,发现该题的主要操作是路径的输出,因此采用边集数组(每个元素是一个结构体,包括起点、终点和权值)和邻接矩阵比较方便以后的编程。
其次是kruskal算法。
该算法的主要步骤是:GENERNIC-MIT(G,W)1. A←2. while A没有形成一棵生成树3 do 找出A的一条安全边(u,v);4.A←A∪{(u,v)};5.return A算法设置了集合A,该集合一直是某最小生成树的子集。
在每步决定是否把边(u,v)添加到集合A中,其添加条件是A∪{(u,v)}仍然是最小生成树的子集。
我们称这样的边为A 的安全边,因为可以安全地把它添加到A中而不会破坏上述条件。
然后就是Dijkstra算法。
Dijkstra算法基本思路是:假设每个点都有一对标号 (dj , pj),其中dj是从起源点s到点j的最短路径的长度 (从顶点到其本身的最短路径是零路(没有弧的路),其长度等于零);pj则是从s到j的最短路径中j点的前一点。
求解从起源点s到点j的最短路径算法的基本过程如下:1) 初始化。
起源点设置为:① ds =0, ps为空;②所有其他点: di=∞, pi=?;③标记起源点s,记k=s,其他所有点设为未标记的。
2) 检验从所有已标记的点k到其直接连接的未标记的点j的距离,并设置:d j =min[dj, dk+lkj]式中,lkj是从点k到j的直接连接距离。
3) 选取下一个点。
从所有未标记的结点中,选取dj中最小的一个i:di =min[dj, 所有未标记的点j]点i就被选为最短路径中的一点,并设为已标记的。
4) 找到点i的前一点。
从已标记的点中找到直接连接到点i的点j*,作为前一点,设置:i=j*5) 标记点i。
最小生成树克鲁斯卡尔算法
最小生成树克鲁斯卡尔算法
最小生成树克鲁斯卡尔算法是一种基于贪心思想的图论算法,主
要用于解决图的最小生成树问题。
该算法精简高效,在实际应用中广
泛使用。
最小生成树问题是指,在一个带权无向图中,选取一些边,使得
它们组成一棵树,且这棵树的所有边的权值之和最小。
这个问题可以
用克鲁斯卡尔算法来解决。
克鲁斯卡尔算法的思想是,首先将所有边按照权值从小到大排序,依次将每条边加入到已选的边集合中,如果加入该边后形成了环路,
则不选择该边。
最终生成的边集合就是该图的最小生成树。
这个算法的时间复杂度为O(ElogE),其中E为边数。
虽然速度不
如其他复杂度更快的算法,但克鲁斯卡尔算法的代码简洁易懂,并且
适用于边数较小的图,正因为如此,在实际应用中它的使用非常广泛。
在大型计算机网络中,最小生成树算法常用于广域网的拓扑设计
问题。
在城市交通规划中,也可以应用最小生成树算法,来设计更加
合理的交通路线。
需要注意的是,最小生成树仅仅是起到了将所有节点连接起来的
作用,它并不保证任意两个节点之间都有最短路径。
如果需要求解两
点间的最短路径问题,需要使用单源最短路径算法,如Dijkstra算法
和Bellman-Ford算法等。
总之,最小生成树克鲁斯卡尔算法在实际应用中扮演着重要的角色,尤其在计算机网络和城市规划领域的应用非常广泛。
学习并掌握这个算法,对于解决实际问题具有重要的指导意义。
图的基本操作与kruskal最小生成树实验报告
数据结构实验五图的基本操作一、实验目的1、使学生可以巩固所学的有关图的基本知识。
2、熟练掌握图的存储结构。
3、熟练掌握图的两种遍历算法。
二、实验内容[问题描述]对给定图,实现图的深度优先遍历和广度优先遍历。
[基本要求]以邻接表为存储结构,实现连通无向图的深度优先和广度优先遍历。
以用户指定的结点为起点,分别输出每种遍历下的结点访问序列。
【测试数据】由学生依据软件工程的测试技术自己确定。
三、实验前的准备工作1、掌握图的相关概念。
2、掌握图的逻辑结构和存储结构。
3、掌握图的两种遍历算法的实现。
四、详细设计五、源程序#define INFINITY 10000#define MAX_VERTEX_NUM 40#define MAX 40#include<stdlib.h>#include<stdio.h>#include<conio.h>#include<string.h>typedef struct ArCell{int adj;}ArCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; typedef struct{char name[20];}infotype;typedef struct{infotype vexs[MAX_VERTEX_NUM];AdjMatrix arcs;int vexnum,arcnum;}MGraph;int LocateVex(MGraph *G,char* v){ int c=-1,i;for(i=0;i<G->vexnum;i++)if(strcmp(v,G->vexs[i].name)==0){c=i;break;}return c;}MGraph * CreatUDN(MGraph *G)//初始化图,接受用户输入{int i,j,k,w;char v1[20],v2[20];printf("请输入图的顶点数,弧数:");scanf("%d%d",&G->vexnum,&G->arcnum);printf("结点名字:\n");for(i=0;i<G->vexnum;i++){printf("No.%d:",i+1);scanf("%s",G->vexs[i].name);}for(i=0;i<G->vexnum;i++)for(j=0;j<G->vexnum;j++)G->arcs[i][j].adj=INFINITY;printf("请输入一条边依附的两个顶点和权值:\n");for(k=0;k<G->arcnum;k++){printf("第%d条边:\n",k+1);printf("起始结点:");scanf("%s",v1);printf("结束结点:");scanf("%s",v2);printf("边的权值:");scanf("%d",&w);i=LocateVex(G,v1);j=LocateVex(G,v2);if(i>=0&&j>=0){G->arcs[i][j].adj=w;G->arcs[j][i]=G->arcs[i][j];}}return G;}int FirstAdjVex(MGraph *G,int v){int i;if(v<=0 &&v<G->vexnum){ //v合理for(i=0;i<G->vexnum;i++)if(G->arcs[v][i].adj!=INFINITY)return i;}return -1;}void VisitFunc(MGraph *G,int v){printf("%s ",G->vexs[v].name);}int NextAdjVex(MGraph *G,int v,int w){int k;if(v>=0 && v<G->vexnum && w>=0 && w<G->vexnum)//v,w合理{for( k=w+1;k<G->vexnum;k++)if(G->arcs[v][k].adj!=INFINITY)return k;}return -1;}int visited[MAX];void DFS(MGraph *G,int v)//从第v个顶点出发递归地深度优先遍历图G {int w;visited[v]=1;VisitFunc(G,v);//访问第v个结点for(w=FirstAdjVex(G,v);w>=0;w=NextAdjVex(G,v,w))if(!visited[w]){DFS(G,w);printf("%d ",G->arcs[v][w].adj);}}void DFSTraverse(MGraph *G,char *s)//深度优先遍历{int v,k;for(v=0;v<G->vexnum;v++)visited[v]=0;k=LocateVex(G,s);if(k>=0&&k<G->vexnum){for(v=k;v>=0;v--){if(!visited[v])DFS(G,v);}for(v=k+1;v<G->vexnum;v++)if(!visited[v])DFS(G,v);}}typedef struct Qnode{int vexnum;struct Qnode *next;}QNode,*QueuePtr;typedef struct{QueuePtr front;QueuePtr rear;}LinkQueue;int InitQueue(LinkQueue *Q){Q->front=Q->rear=(QueuePtr)malloc(sizeof(QNode)); if(!Q->front)exit(0);Q->front->next=NULL;return 1;}void EnQueue(LinkQueue *Q,int a ){QueuePtr p;p=(QueuePtr)malloc(sizeof(QNode));if(!p)exit(0);p->vexnum=a;p->next=NULL;Q->rear->next=p;Q->rear=p;}int DeQueue(LinkQueue *Q,int *v){ QueuePtr p;if(Q->front==Q->rear){printf("结点不存在!\n");exit(0);}p=Q->front->next;*v=p->vexnum;Q->front->next=p->next;if(Q->rear==p)Q->front=Q->rear;return *v;}int QueueEmpty(LinkQueue *Q){if(Q->rear==Q->front)return 0;return 1;}int Visited[MAX];void BFSTraverse(MGraph *G,char *str)//广度优先遍历{int w,u,v,k;LinkQueue Q,q;for(v=0;v<G->vexnum;v++) Visited[v]=0;InitQueue(&Q);InitQueue(&q);k=LocateVex(G,str);for(v=k;v>=0;v--)if(!Visited[v]){Visited[v]=1;VisitFunc(G,v);EnQueue(&Q,v);//v入队while(!QueueEmpty(&Q)){DeQueue(&Q,&u);//出队for(w=FirstAdjVex(G,u);w>=0;w=NextAdjVex(G,u,w)) if(!Visited[w]){Visited[w]=1;VisitFunc(G,v);EnQueue(&Q,w);}}}for(v=k+1;v<G->vexnum;v++)if(!Visited[v]){Visited[v]=1;VisitFunc(G,v);EnQueue(&Q,v);//v入队while(!QueueEmpty(&Q)){DeQueue(&Q,&u);//出队for(w=FirstAdjVex(G,u);w>=0;w=NextAdjVex(G,u,w)) if(!Visited[w]){Visited[w]=1;VisitFunc(G,v);EnQueue(&Q,w);}}}}void main(){MGraph *G,b;char v[10];G=CreatUDN(&b);printf("请输入起始结点名称:");scanf("%s",v);printf("\n深度优先遍历:\n");DFSTraverse(G,v);printf("\n广度优先遍历:\n");BFSTraverse(G,v);getch();}六、测试数据及调试实验六图的应用一、实验目的1、使学生可以巩固所学的有关图的基本知识。
离散数学--最小生成树实验报告
创作编号:GB8878185555334563BT9125XW创作者:凤呜大王*一、实验目的:掌握图的存储表示和以及图的最小生成树算法。
二、实验内容:1.实现图的存储,并且读入图的内容。
2.利用克鲁斯卡尔算法求网络的最小生成树。
3.实现构造生成树过程中的连通分量抽象数据类型。
4.以文本形式输出对应图的最小生成树各条边及权值。
三、实验要求:1.在上机前写出全部源程序;2.能在机器上正确运行程序;3.用户界面友好。
需求分析:1、利用克鲁斯卡尔算法求网的最小生成树;2、以用户指定的结点为起点,分别输出每种遍历下的结点访问序列;3、输入为存在边的顶点对,以及它们之间的权值;输出为所得到的邻接矩阵以及按权排序后的边和最后得到的最小生成树;克鲁斯卡尔算法:假设WN=(V,{E}) 是一个含有n 个顶点的连通网,按照构造最小生成树的过程为:先构造一个只含n 个顶点,而边集为空的子图,之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。
依次类推,直至只有一棵树,也即子图中含有n-1条边为止。
测试数据:自行指定图进行运算四、详细设计源程序#include<stdio.h>#include<stdlib.h>#define M 20#define MAX 20typedef struct{int begin;int end;int weight;}edge;typedef struct{int adj;int weight;}AdjMatrix[MAX][MAX];typedef struct{AdjMatrix arc;int vexnum, arcnum;}MGraph;void CreatGraph(MGraph *);void sort(edge* ,MGraph *);void MiniSpanTree(MGraph *);int Find(int *, int );void Swapn(edge *, int, int);void CreatGraph(MGraph *G)创作编号:GB8878185555334563BT9125XW创作者:凤呜大王*{int i, j,n, m;printf("请输入边数和顶点数:");scanf("%d %d",&G->arcnum,&G->vexnum);for (i = 1; i <= G->vexnum; i++){for ( j = 1; j <= G->vexnum; j++){G->arc[i][j].adj = G->arc[j][i].adj = 0;}}for ( i = 1; i <= G->arcnum; i++){printf("\n请输入有边的2个顶点");scanf("%d %d",&n,&m);while(n < 0 || n > G->vexnum || m < 0 || n > G->vexnum){printf("输入的数字不符合要求请重新输入:");scanf("%d%d",&n,&m);}G->arc[n][m].adj = G->arc[m][n].adj = 1;getchar();printf("\n请输入%d与%d之间的权值:", n, m);scanf("%d",&G->arc[n][m].weight);}printf("邻接矩阵为:\n");for ( i = 1; i <= G->vexnum; i++){for ( j = 1; j <= G->vexnum; j++){printf("%d ",G->arc[i][j].adj);}printf("\n");}}void sort(edge edges[],MGraph *G){int i, j;for ( i = 1; i < G->arcnum; i++){for ( j = i + 1; j <= G->arcnum; j++){if (edges[i].weight > edges[j].weight){Swapn(edges, i, j);}}}printf("权排序之后的为:\n");for (i = 1; i < G->arcnum; i++){printf("<< %d, %d >> %d\n", edges[i].begin, edges[i].end, edges[i].weight);}}void Swapn(edge *edges,int i, int j){创作编号:GB8878185555334563BT9125XW创作者:凤呜大王*int temp;temp = edges[i].begin;edges[i].begin = edges[j].begin;edges[j].begin = temp;temp = edges[i].end;edges[i].end = edges[j].end;edges[j].end = temp;temp = edges[i].weight;edges[i].weight = edges[j].weight;edges[j].weight = temp;}void MiniSpanTree(MGraph *G){int i, j, n, m;int k = 1;int parent[M];edge edges[M];for ( i = 1; i < G->vexnum; i++){for (j = i + 1; j <= G->vexnum; j++) {if (G->arc[i][j].adj == 1){edges[k].begin = i;edges[k].end = j;edges[k].weight = G->arc[i][j].weight;k++;}}}sort(edges, G);for (i = 1; i <= G->arcnum; i++){parent[i] = 0;}printf("最小生成树为:\n");for (i = 1; i <= G->arcnum; i++){n = Find(parent, edges[i].begin);m = Find(parent, edges[i].end);if (n != m){parent[n] = m;printf("<< %d, %d >> %d\n", edges[i].begin, edges[i].end, edges[i].weight);}}}int Find(int *parent, int f){while ( parent[f] > 0){f = parent[f];}return f;}int main(void){MGraph *G;G = (MGraph*)malloc(sizeof(MGraph));if (G == NULL){printf("memory allcation failed,goodbye");exit(1);}CreatGraph(G);创作编号:GB8878185555334563BT9125XW创作者:凤呜大王*MiniSpanTree(G);system("pause");return 0;}运行结果:五、实验总结(结果分析和体会)在编程时,因为考虑的情况比较多,所以容易造成错误和遗漏,为了避免这些问题的出现,可以先用笔把所有的程序在纸上,然后再根据列表编写程序,这样不仅简单易懂,还避免了一些不必要的错误。
求最小生成树(Kruskal算法)实验报告【范本模板】
学生实验报告学院:软件与通信工程学院课程名称:离散数学(软件)专业班级:12软件2班姓名:杨滨学号:0123707学生实验报告(2) 学生姓名杨滨 学号 0123707 同组人 实验项目 求最小生成树(Kruskal 算法)□必修 □选修 □演示性实验 □验证性实验 □操作性实验 □综合性实验 实验地点W101 实验仪器台号 指导教师 赵晓平 实验日期及节次 2013。
12。
12(四) 89A 节一、实验综述1、实验目的及要求(1)了解求最优化问题的贪心算法,了解贪心法的基本要素,学会如何使用贪心策略设计算法;(2)掌握Prim 算法和Kruskal 算法的思想及两者之间的区别;(3)编写程序,分别利用Prim 算法和Kruskal 算法实现,求出最小代价生成树,输出构成最小代价生成树的边集.实验要求:给出如右图的边权图,求最小生成树.认真完成实验题,能正确运行,提交实验报告并上传程序,实验报告要求写出操作步骤、结果、问题、解决方法、体会等.2、实验仪器、设备或软件计算机、VC++6。
0、office 、相关的操作系统等.二、实验过程(实验步骤、记录、数据、分析)#include<stdio.h 〉#define VERTS 6struct edge {int from ,to; //起顶点,终顶点int find ,val ; //标记,顶点间边长struct edge *next; };typedef struct edge node;node *find_min_cost(node *);void min_tree(node *);√ √int v[VERTS+1]={0};//记录顶点即下标,值即出现过的次数void main(){int data[10][3]={{1,0,6},{0,3,5},{3,5,2},{5,4,6},{4,1,3},{2,1,5},{2,0,1},{2,3,5},{2,5,4},{2,4,6}};//表示有10条线,例如{1,0,6}表示1和0距离为6node *head,*ptr,*new_node;head=NULL;printf(”Add graph:\n”);for (int i=0; i<10; i++){for (int j=1;j<=VERTS;j++){if (data[i][0]==j){new_node=new node;new_node—>from=data[i][0];new_node—〉to=data[i][1];new_node—〉val=data[i][2];new_node—>find=0;new_node-〉next=NULL;if (head==NULL){head=new_node;head-〉next=NULL;ptr=head;}else{ptr-〉next=new_node;ptr=ptr-〉next;}}}}for (ptr=head; ptr!=NULL; ptr=ptr—>next)printf("Begin[%d]\tEnd[%d]\t\tPath[%d]\n”,ptr-〉from,ptr—〉to,ptr-〉val);printf("\nAdd Min Tree:\n”);min_tree(head);putchar('\n');}void min_tree(node *head) //建立最小生成树{node *ptr,*tmptr;int result=0;//布尔值,是否做出输出结果for (ptr=head;ptr!=NULL;ptr=ptr—〉next) //遍历十条边{tmptr=find_min_cost(head); //找出最小边顶点v[tmptr-〉from]++;//当前起点已选自加1次v[tmptr—〉to]++; //当前终点已选自加1次if (v[tmptr—>from]〉1 &&v[tmptr->to]>1){/*这个IF语句就是一个防止生成树中有回路;因为v[tmptr->from]与v[tmptr—〉to]都出现过一次,这棵树的叶子结点都为1,所以当两个结点同时为2时,就形成回路*/v[tmptr—〉from]——;v[tmptr—>to]--;result=1;}else result=0;if (result==0)printf("Begin[%d]\tEnd[%d]\t\tPath[%d]\n",tmptr—〉from,tmptr->to,tmptr—〉val);}}node *find_min_cost(node *head) //最小边查找{int min_val=100;node *ptr,*tmptr;for (ptr=head;ptr!=NULL;ptr=ptr-〉next){if (ptr->val<min_val && ptr-〉find==0) //当当前边最小时并当前顶点没有被选择过{min_val=ptr->val;tmptr=ptr;}} //整个循环遍历完毕找到最小连min_valtmptr->find=1; //并标记该顶的边已选择return tmptr;}三、结论1、实验结果2、分析讨论在编写这个程序时,我们先要非常熟悉kruskal算法,kruskal算法总共选择n—1条边,所使用的贪婪准则是:从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合内。
克鲁斯卡尔(Kruskal)算法求无向网的最小生成树数据结构报告
数据结构上机报告(1) 姓名:张可心学号:14030188030 班级:1403018一、题目描述用克鲁斯卡尔(Kruskal)算法求无向网的最小生成树,分析你的算法的时空复杂度,必须有过程说明。
输入:输入数据第一行为两个正整数n和m,分别表示顶点数和边数。
后面紧跟m行数据,每行数据是一条边的信息,包括三个数字,分别表示该边的两个顶点和边上的权值。
输出:按顺序输出Kruskal算法求得的最小生成树的边集,每行一条边,包括三个数字,分别是该边的两个顶点和边上的权值,其中第一个顶点的编号应小于第二个顶点的编号。
示例输入8 111 2 31 4 51 6 182 4 72 5 63 5 103 8 204 6 154 7 115 7 85 8 12示例输出1 2 31 4 52 5 65 7 83 5 105 8 124 6 15二、解题思路(1)假定每对顶点表示图的一条边,每条边对应一个权值;(2)输入每条边的顶点和权值;(3)输入每条边后,计算出最小生成树;(4)打印最小生成树边的顶点及权值。
三、源代码#include<stdio.h>#include<stdlib.h>typedef struct {int a,b,value;}node;int stcmp(const void *p,const void *q){node *a=(node *)p;node *b=(node *)q;if(a->value>b->value){return 1;}else if(a->value==b->value){return 0;}else{return -1;}}int root(int a,int *b){for(;a!=b[a];a=b[a]);return a;}bool isgroup(int a,int b,int *c){if(root(a,c)==root(b,c))return true;return false;}void add(int a,int tob,int *c){c[root(a,c)]=root(tob,c);}int main (){int n,m;scanf("%d %d",&n,&m);node no[m];for(int u=0;u<m;u++){scanf("%d",&(no[u].a));scanf("%d",&(no[u].b));scanf("%d",&(no[u].value));}qsort(no,m,sizeof(no[0]),stcmp);int bcj[n+1];for(int u=1;u<=n;u++){bcj[u]=u;}int i=0;int cc=n;for(;i<m;i++){if(!isgroup(no[i].a,no[i].b,bcj)){add(no[i].a,no[i].b,bcj);cc--;printf("%d %d %d\n",no[i].a,no[i].b,no[i].value);}if(cc==1)break;}return 0;}四、运行结果。
克鲁斯卡尔(Kruskal)算法的实现与分析
克鲁斯卡尔(Kruskal)算法的实现与分析作者:虎治勤来源:《电脑知识与技术·学术交流》2008年第11期摘要:克鲁斯卡尔(Kruskal)算法是实现图的最小生成树最常用的算法。
本文主要介绍克鲁斯卡尔(Kruskal)算法的实现方法,并对克鲁斯卡尔(Kruskal)算法的效率进行分析。
关键词:克鲁斯卡尔(Kruskal)算法;算法实现;算法分析中图分类号:TP301文献标识码:A文章编号:1009-3044(2008)11-20311-021 克鲁斯卡尔(Kruskal)算法的基本思想设有一个有n个顶点的连通网络N={V,E},最初先构造一个只有n个顶点,没有边的非连通图T={V,Ø},图中每个顶点自成一个连通分量。
当在E中选到一条具有最小权值的边时,若该边的两个顶点落在不同的连通分量上,则将此边加入到T中;否则将此边舍去,重新选择一条权值最小的边。
如此重复下去,直到所有顶点在同一个连通分量上为止。
算法的基本框架:利用最小堆(MinHeap)和并查集(DisjointSets)来实现克鲁斯卡尔(Kruskal)算法。
2 应用克鲁斯卡尔(Kruskal)算法构造最小生成树的实例建立最小生成树的实现过程:出堆顺序:(0,5,10)选中,(2,3,12)选中,(1,6,14)选中,(1,2,16)选中,(3,6,18)舍弃,(3,4,22)选中,(4,6,24)舍弃,(4,5,25)选中。
邻接矩阵表示:3 克鲁斯卡尔(Kruskal)算法的实现首先,利用最小堆来存放E中的所有的边,堆中每个结点的格式为:在构造最小生成树的过程中,最小堆中存放剩余的边,并且利用并查集的运算检查依附于一条边的两个顶点tail、head是否在同一个连通分量(即并查集的同一个子集合)上,是则舍去这条边;否则将此边加入T,同时将这两个顶点放在同一个连通分量上。
随着各边逐步加入到最小生成树的边集合中,各连通分量也在逐步合并,直到形成一个连通分量为止。
克鲁斯卡尔算法 任意两节点最小路径
克鲁斯卡尔算法任意两节点最小路径克鲁斯卡尔算法是一种常用的最小生成树算法,主要用于寻找一个带权无向连通图的最小生成树。
在这篇文章中,我们将详细介绍克鲁斯卡尔算法的原理和步骤,并以任意两个节点之间的最小路径问题为应用场景来说明算法的实际应用。
让我们一步一步地回答这个问题。
首先,让我们先了解一下什么是最小生成树。
在一个连通图中,最小生成树是指包含图中所有顶点的一个连通子图,且该子图是一个树,树上的边的权值之和最小。
最小生成树有多种生成方式,克鲁斯卡尔算法就是其中一种。
那么,克鲁斯卡尔算法是如何工作的呢?它主要分为以下4个步骤:1. 初始化:首先,将图中的所有边按照权值从小到大进行排序。
2. 创建森林:从权值最小的边开始,依次将边加入最小生成树的集合中,确保不会形成环路。
3. 合并集合:当最小生成树的集合中边的数量等于节点数量减1时,算法停止;否则,继续执行。
4. 得到最小生成树:最终,集合中的边就构成了最小生成树。
现在,我们将这个算法应用到任意两个节点之间的最小路径问题中。
假设我们有一个城市网络图,其中城市间的道路连接表示为带有权值的边。
我们的目标是找到任意两个城市之间的最短路径。
首先,我们将这个城市网络图表示为一个无向带权连通图。
整个算法的核心思想是通过构建最小生成树来找到最短路径。
因此,我们需要将这个城市网络图转化为一个表示道路连接的图。
接下来,按照克鲁斯卡尔算法的步骤,我们首先对城市网络图的边按照权值进行排序。
然后,我们从权值最小的边开始,依次将边加入最小生成树的集合中。
但是,我们需要在加入边之前进行判断,以确保加入边不会形成环路。
这就需要使用并查集这种数据结构来管理节点之间的连通关系。
在加入边之前,我们需要判断边的两个节点是否已经属于同一个集合。
如果是,说明加入这条边会形成环路,我们就不加入这条边。
如果不是,我们可以将这条边加入集合,并且合并边的两个节点所属的集合。
当最小生成树的集合中边的数量等于节点数量减1时,算法停止,我们得到了最小生成树。
克鲁斯卡尔算法求最小生成树的最短路径
克鲁斯卡尔算法求最小生成树的最短路径在计算机科学领域中,克鲁斯卡尔算法是一种经典的图论算法,用于求解最小生成树和最短路径的问题。
在本文中,我将深入探讨克鲁斯卡尔算法的原理和应用,以及其在实际生活中的意义和影响。
我将从简单的概念入手,逐步深入,帮助你更好地理解和掌握这一重要的算法。
1. 算法原理克鲁斯卡尔算法是一种基于贪心策略的算法,用于求解带权无向连通图的最小生成树。
其基本思想是从图中的所有边中选择权值最小的边,且保证不构成回路,重复这个过程直到所有的顶点都已经联通。
通过这种方式,最终得到的就是图的最小生成树。
算法的具体步骤可以分为以下几个部分:- 将图中的所有边按照权值进行排序- 依次考虑所有的边,若当前边的两个顶点不属于同一连通分量,则将其加入最小生成树中- 重复上述步骤直到所有的顶点都已经联通2. 算法应用克鲁斯卡尔算法在实际生活中有着广泛的应用。
以通信网络建设为例,假设我们需要在若干个城市之间铺设光纤网络,我们希望在网络总成本最低的前提下建立通信链路。
这时候,我们可以将城市看作图的顶点,城市之间的光缆看作边的权值,然后利用克鲁斯卡尔算法求解最小生成树,就可以得到一个在总成本最低的情况下连接所有城市的方案。
3. 个人理解对于我个人而言,克鲁斯卡尔算法是一种非常优雅的算法。
它简单而高效地解决了一个看似复杂的问题,展现了计算机科学中贪心策略的魅力。
在学习和了解这一算法的过程中,我深刻体会到了算法设计的巧妙以及数学结构在计算机科学中的重要性。
我也意识到算法并不仅仅是理论上的概念,它们在实际生活中有着广泛的应用,并对我们的生活产生着深远的影响。
在本文中,我对克鲁斯卡尔算法的原理和应用进行了深入的探讨,希望能够帮助读者更好地理解和掌握这一算法。
通过分析算法的原理和应用,我相信读者们将对克鲁斯卡尔算法有更深入的理解,并能够在实际问题中灵活运用这一算法。
希望本文能够为读者们带来一些启发和帮助,让我们一起探索和学习计算机科学的奥秘吧!克鲁斯卡尔算法的应用广泛,不仅仅局限于通信网络建设,还涉及到诸如城市规划、交通规划、电力系统设计等领域。
克鲁斯卡尔算法实现最小生成树
克鲁斯卡尔算法姓名:学号:班级:信工0808 成绩:1,原理由于克鲁斯卡尔算法是在图中从边长小的边开始查找,为了减少重复查找与比较的次数,直接使用快排,使边长按非降序排列。
为了使所构成的最小生成树不出现回路,则对顶点进行集合划定,father[i]保存顶点i所在的集合序号。
初始时每个顶点对应的集合序号为其顶点序号,只要两顶点a,b不再同一集合内,即可加入到最小生成树中,若father[a]<father[b]则b所在的集合序号全部改为father[a],否则,a所在的集合序号全部改为father[b]。
主要类型说明:2,struct edge{int pointA;//边的起点int pointB;//边的终点int cost;//边长};3程序代码#include<iostream>#include<iomanip>using namespace std;const int N = 6;const int MaxCost = 100;int m;//边长不为0且不重复的边的条数-1struct edge{int pointA;int pointB;int cost;};const int size = (N*N-N)/2;//无重复的边的条数,边长可能为0edge edgeList[size];//排好序的边的信息int father[N+1];//father[i]为顶点i所在的集合,按所在集合中顶点序号最小来赋值//edgecost[i][j]为顶点i+1到顶点j+1的距离int edgecost[N][N] = {0,6,1,5,0,0, 6,0,5,0,3,0, 1,5,0,5,6,4,5,0,5,0,0,2, 0,3,6,0,0,6, 0,0,4,2,6,0};void print(int edgecost[N][N]){cout<<" ";for (int i = 1; i<=N;i++){cout<<setw(4)<<i<<" ";}cout<<endl;for (int a = 0; a < N; a++){cout<<a+1<<" ";for (int b = 0; b < N; b++)cout<<setw(4)<<edgecost[a][b]<<" ";cout<<endl;}}void init(){for (int a = 1; a<=N;a++){father[a] = a;}m = 0;for (int i = 0;i<N;i++)for (int j = i+1; j<N;j++){if(edgecost[i][j] != 0){edgeList[m].cost = edgecost[i][j];}else{edgeList[m].cost = MaxCost;}edgeList[m].pointA = i+1;edgeList[m].pointB = j+1;m++;}}inline void exchang(edge &b,edge &a){edge temp;temp.cost = b.cost;temp.pointA = b.pointA;temp.pointB = b.pointB;b.cost = a.cost;b.pointA = a.pointA;b.pointB = a.pointB;a.cost = temp.cost;a.pointA = temp.pointA;a.pointB = temp.pointB;}int partion(edge * pa,int a,int b){edge * Num = pa+a;int i= a;int j= b;while(i<j){while((i<j)&(pa[i].cost<=Num->cost)) {i++;}while((i<j)&(pa[j].cost>Num->cost)) {j--;}exchang(pa[i],pa[j]);}if(pa[a].cost>pa[i].cost){exchang(pa[a],pa[i]);}return i;}void quick_sort(edge * p,int a,int b){if(a>=b) return;int r= partion(p,a,b);quick_sort(p,a,r-1);quick_sort(p,r,b);}void getMinTree(){int e =0;int a =0,b =0;int i = 0;int s;while(e<N-1){a = edgeList[i].pointA;b = edgeList[i].pointB;if ( father[a] != father[b])//a,b不在同一个集合中则不会形成回路{if (father[a] > father[b]){for (s = 1; s<=N; s++)if (father[s] == father[a] )father[s] = father[b];}else{for (s = 1; s<=N; s++)if (father[s] == father[b] )father[s] = father[a];}cout<<"<"<<a<<" , "<<b<<">"<<endl;e++;}i++;}}void main(){init();print(edgecost);quick_sort(edgeList,0,size-1);getMinTree();}4,程序结果。
克鲁斯卡尔算法求最小生成树
目录1.需求分析 (2)1.1 设计题目 (2)1.2 设计任务及要求 (2)1.3课程设计思想 (2)1.4 程序运行流程 (2)1.5软硬件运行环境及开发工具 (2)2.概要设计 (2)2.1流程图 (2)2.2抽象数据类型MFSet的定义 (3)2.3主程序 (4)2.4抽象数据类型图的定义 (4)2.5抽象数据类型树的定义 (5)3.详细设计 (7)3.1程序 (7)4.调试与操作说明 (10)4.1测试结果 (10)4.2调试分析 (11)5.课程设计总结与体会 (11)5.1总结 (11)5.2体会 (11)6. 致谢 (12)7. 参考文献 (12)1.需求分析1.1 设计题目:最小生成树1.2 设计任务及要求:任意创建一个图,利用克鲁斯卡尔算法,求出该图的最小生成树。
1.3 课程设计思想:Kruskal算法采用了最短边策略(设G=(V,E)是一个无向连通网,令T=(U,TE)是G的最小生成树。
最短边策略从TE={}开始,每一次贪心选择都是在边集E中选择最短边(u,v),如果边(u,v)加入集合TE中不产生回路,则将边(u,v)加入边集TE中,并将它在集合E中删去。
),它使生成树以一种任意的方式生长,先让森林中的树木随意生长,每生长一次就将两棵树合并,最后合并成一棵树。
1.4程序运行流程:1)提示输入顶点数目;2)接受输入,按照项目要求产生边权值的随机矩阵;然后求解最小生成树;3)输出最小生成树并且退出;1.5 软硬件运行环境及开发工具:VC2.概要设计2.1流程图图1流程图2.2抽象数据类型MFSet的定义:ADT MFSet {数据对象:若设S是MFSet型的集合,则它由n(n>0)个子集Si(i = 1,2...,n)构成,每个子集的成员代表在这个子集中的城市。
数据关系: S1 U S2 U S3 U... U Sn = S, Si包含于S(i = 1,2,...n)Init (n): 初始化集合,构造n个集合,每个集合都是单成员,根是其本身。
数据结构最小生成树克鲁斯卡尔算法的实现资料
摘要设计了一个用C/C++编写程序实现克鲁斯卡尔最小生成树算法,该程序操作简单,界面清晰,易于为用户所接受。
关键词:克鲁斯卡尔,邻接矩阵,最小生成树,vc++。
目录1 课题描述 (1)2 问题分析和任务定义 (2)3 逻辑设计 (3)4 详细设计 (4)5 程序编码 (11)6 程序调试与测试 (17)7 结果分析 (19)8 总结 (20)参考文献 (21)1课题描述用C/C++编写程序实现克鲁斯卡尔最小生成树算法。
假设要在n 个城市之间建立通讯联络网,则连通n个城市只需要n-1条线路。
这是我们设计一个最小生成树的程序用来算出最节省经费的前提下建立这个通信站。
2问题分析和任务定义假设连通网N=(V,{E}),则令最小生成树的初始状态为只有n 个顶点而无边的非连通图T=(V,{}),图中每个顶点自成一个连通分量。
在E中选择代价最小的边,若该边依附的顶点落在T中不同的连通分量上,则将此边加入到T中,否则舍去此边而选择下一条代价最小的边。
依次类推,直到T中所有顶点都在同一连通分量上为止。
设计思想:采用邻接矩阵来存储图,然后采用克鲁斯卡尔算法求出最小生成树。
1).定义结构体。
2).采用邻接矩阵做存储结构创建图(函数模块一)。
3).采用克鲁斯卡尔算法求出该图的最小生成树(函数模块二)。
4).在主函数里面分别调用以上各个函数,最终实现设计目的。
4.1.程序结构·函数CreateMGraph用来实现图的创建,以及图的相关信息的存储。
图的存储采用邻接矩阵存储结构。
·函数minitree_KRUSKAL用来求图的最小生成树。
图的最小生成树有普利姆算法和克鲁斯卡尔算法可以实现,本段代码使用的是克鲁斯卡尔算法,这也是本题所要求使用的。
·各个函数间的联系先调用函数CreateMGraph实现图的创建,然后调用函数minitree_KRUSKAL求出该图的最小生成树4.2.设计说明·在开始的时候添加一些限制条件方便函数的功能实现例如:#define MaxVertexNum 100 //最大顶点个数#define QueueSize 30#define M 30·模块一:图的创建·结构体定义为:typedef struct{VertexType vexs[MaxVertexNum]; //顶点表Link edges[MaxVertexNum][MaxVertexNum]; //图中当前的相连接的两个顶点int n,e; //图中当前的顶点数和边数}MGraph;·函数定义为:MGraph CreateMGraph(){MGraph G;int i,j,k,ch3;char ch1,ch2;printf("请输入该图的顶点数和边数:\n");scanf("%d,%d",&(G.n),&(G.e));printf("请输入该图的顶点信息:\n");for(i=1;i<=G.n;i++){getchar();scanf("%c",&(G.vexs[i]));}for(i=1;i<=G.n;i++)for(j=1;j<=G.n;j++)G.edges[i][j].w=0;printf("请输入该图每条边对应的两个顶点的名称:\n");for(k=1;k<=G.e;k++){scanf("%c",&ch1);printf("请输入第%d条边的顶点序号:",k);scanf("%c %c",&ch1,&ch2);printf("请输入第%d条边的权值大小:",k);scanf("%d",&ch3);for(i=1;ch1!=G.vexs[i];i++);for(j=1;ch2!=G.vexs[j];j++);e[p].vexh=i;e[p].vext=j;e[p].weight=G.edges[i][j].w=ch3; //权值e[p].flag=0;p++;}return G;}流程如图4.1所示创建图使用的是函数MGraph CreateMGraph(),该图的存储结构是邻接矩阵,先对图的结构体进行定义,再进行初始化。
贪心算法Dijkstra普里姆(Prim)克鲁斯卡尔算法,最短路算法
实验(实训)报告辽宁科技大学学院(系)专业时间:课程名称:算法实验(实训)题目:贪心算法的应用班级:姓名学号机台号任课程教师实验(实训)目的:1.掌握贪心算法的基本要素,包括贪心选择性质与最优子结构性质。
2.掌握贪心算法解决问题的基本步骤,并利用它解决实际问题。
3.熟练掌握Dijkstra、普里姆(Prim)与克鲁斯卡尔算法(Kruskal)。
实验(实训)原理、装置、内容、步骤、数据记录及结果分析:1.已知3个物体与一个背包,物体重量为W=(w1,w2,w3)=(18,15,10),价值P=(p1,p2,p3)=(25,24,15),背包的容量为M=20, 物品可以分割成任意大小,要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
答案为(0,1,1/2),最优值为31.5。
#include<stdio.h>void Sort(int n,float v[],float w[]){int i;float t;for(i=1;i<n;i++)if(v[i]>v[i+1]) {t=v[i];v[i]=v[i+1];v[i+1]=t;}for(i=1;i<n;i++)if(w[i]>w[i+1]) {t=w[i];w[i]=w[i+1];w[i+1]=t;}}void Knapsack(int n,float M,float v[],float w[],float x[]){Sort(n,v,w);for (int i=1;i<=n;i++) x[i]=0;float c=M;for (i=1;i<=n;i++){if (w[i]>c) break;x[i]=1;c-=w[i];}if (i<=n) x[i]=c/w[i];for(i=1;i<=n;i++)printf("%f\n",x[i]);}void main(){float w[]={0,10,15,18},value=0;float v[]={0,15,24,25};float x[]={0,0,0,0};Knapsack(3,20,v,w,x);for (int i=1;i<4;i++)value=value+x[i]*v[i];printf("Total Value:%f ", value);}4.装载问题描述如下:有一批共n个集装箱要装上艘载重量为c的轮船,其中集装箱i 的重量为w i。
求MST的算法—克鲁斯卡尔 (kruskal
1
10 6 2 12 8
2
9 3
1
6
2
2
1பைடு நூலகம்
6
2 8
2
3
3
7
3
3
3
4
5
4
5
4
5
求MST的算法—克鲁斯卡尔 (kruskal) MST的算法— 算法
关键和难点:如何判断欲加入的一条 边是否与生成树中已保留的边形成回 路?
可以将顶点划分到不同的集合中,每个集合中的 顶点表示一个无回路的连通分量。 很明显,算法开始时,把所有n个顶点划分到n个 集合中,每个集合只有一个顶点,表明顶点之间 互不相通。当选取一条边时,若它的两个顶点分 属于不同的集合,则表明此边连通了两个不同的 连通分量,
求MST的算法—克鲁斯卡尔 (kruskal)算法 MST的算法— (kruskal)算法
设置边的集合TE的初始状态为空集。 设置边的集合TE的初始状态为空集。 将图G 将图G中的边按权值从小到大排序,然后从小 的开始依次选取,若选取的边使生成树T 的开始依次选取,若选取的边使生成树T不形 成回路,则把它并入TE中,保留作为T 成回路,则把它并入TE中,保留作为T的一条 边;若选取的边使生成树T 边;若选取的边使生成树T不形成回路,则将 其舍弃; 如此进行下去,直到TE中包含n 如此进行下去,直到TE中包含n-1条边为止。
j
1
2
3 4
山东大学算法实验3
山东大学软件学院算法设计与分析课程实验报告
实验步骤与内容:
实验内容:
prim算法
基本思想:假设G=(V,E)是连通的,TE是G上最小生成树中边的集合。
算法从U={u0}(u0∈V)、TE={}开始。
重复执行下列操作:
在所有u∈U,v∈V-U的边(u,v)∈E中找一条权值最小的边(u0,v0)并入集合TE中,同时v0并入U,直到V=U为止。
此时,TE中必有n-1条边,T=(V,TE)为G的最小生成树。
Prim算法的核心:始终保持TE中的边集构成一棵生成树。
算法程序设计
运行结果如下
(}kruskal算法
假设连通网N=(V,{E})。
则令最小生成树的初始状态为只有n个顶点而无边的非连通图T=(V,{}),图中每个顶点自成一个连通分量。
在E中选择最小代价的边,若该边依附的顶点落在T中不同的连通分量中,则将该边加入到T中,否则舍去此边而选择下一条代价最小的边,依次类推,直到T中所有顶点都在同一连通分量上为止。
克鲁斯卡尔算法是一种用来寻找最小生成树的算法。
在剩下的所有未选取的边中,找最小边,如果和已选取的边构成回路,则放弃,选取次小边。
这同样是一个比较难以理解的算法,这一次我又是看了别人的博客才理解了一点。
1.将图的所有连接线去掉,只剩顶点
2.从图的边集数组中找到权值最小的边,将边的两个顶点连接起来
3.继续寻找权值最小的边,将两个顶点之间连接起来,如果选择的边使得最小生成树出现了环路,则放弃该边,选择权值次小的边
4.直到所有的顶点都被连接在一起并且没有环路,最小生成树就生成了。
代码如下:
运行结果如下。
算法实习报告
普里姆(Prim)算法:假设N=(V,{E})是连通网,V={V1,V2,…,Vn}是网的顶点集合,{E}是N上最小生成树中边的集合。
引入顶点集合U和边的集合TE,U的初试状态为{V1},它存放的是当前所得到的(还未完成的)最小代价生成树上所有顶点,TE的初始状态为{}。
在Prim算法的每一步,都从所有的边{(u,v), u U, v V}中找出所有代价最小的边(u,v),同时将v 并入U,(u,v)并入集合TE,直到U=V为止。
此时TE中必有n-1条边,则T=(V,{TE})为N的最小生成树。
克鲁斯卡尔(Kruskal)算法:假设G=(V,{E})是一个连通的无向图,其中V={1,2,…,n},引入辅助集合T,来存放当前所形成的最小生成树的所有边,其初始状态为空,Kruskal算法是逐步给T添加不和T中的边构成回路的当前最小代价边。
具体步骤:1、初始化T={ };2、当T的边小于n-1时,做如下工作;3、从E(G)中选取代价最小的边(v,u)并删除之;4、若(v,u)不和T中的边一起构成回路,则将边(v,u)并入T中。
5、循环2~4步,直到T中所有顶点都在同一个连通图上为止。
拓扑排序的计算机实现:方法:采用邻接表作为有向图的存储结构,且在头结点中增加一个有效顶点的入度,入度为零的顶点既为滑有前趋的顶点,删除顶点及弧可以使入度减1。
为避免重复检测入度为零的顶点,可另设一链表将所有入度为零的顶点链结到一起,每当输出便删除,反之,当有新的入度为零的顶点则插入,这相当于一个栈。
算法:1、查邻接表中入度为零的顶点,并进栈;2、当栈非空时,进行拓扑排序;(1)输出栈顶的顶点Vj并退栈;(2)输出栈顶的顶点Vj的直接后继Vk(k=1,2,…),将Vk的入度减1,并将入度为0的顶点进栈。
(3)若栈空时输出的顶点数不足AOV-网中顶点数n,则说明有向图中存在有向环,否则拓扑排序完毕。
Status TopologicalSort(ALGraph G){FindInDegree(G,indegree);InitStack(S);for(i=0;i<G.Vexnum;++i)if(!indegree(i))push(S,i);count=0;if(!StachEmpty(s)){pop(S,i);printf(i,G.vertices(i),data);count++;for(p=G.vertices[i].firstarc; p ; p=p->nextarc){k=p->adjvex;if(!(--indegree(k)))push(S,K);}}if(count<G.vexnum)return ERROR;elsereturn OK;}迪杰斯拉特(Dijkstra)算法:该算法提出了一个按路径递增的顺序产生最短路径的算法。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
实验报告
实验原理:
Kruskal 算法是一种按照图中边的权值递增的顺序构造最小生成树的方法。
其基本思想是:设无向连通网为G=(V,E),令G 的最小生成树为T,其初态为T=(V,{}),即开始时,最小生成树T 由图G 中的n 个顶点构成,顶点之间没有一条边,这样T 中各顶点各自构成一个连通分量。
然后,按照边的权值由小到大的顺序,考察G 的边集E 中的各条边。
若被考察的边的两个顶点属于T 的两个不同的连通分量,则将此边作为最小生成树的边加入到T 中,同时把两个连通分量连接为一个连通分量;若被考察边的两个顶点属于同一个连通分量,则舍去此边,以免造成回路,如此下去,当T 中的连通分量个数为1 时,此连通分量便为G 的一棵最小生成树。
如教材153页的图4.21(a)所示,按照Kruskal 方法构造最小生成树的过程如图 4.21 所示。
在构造过程中,按照网中边的权值由小到大的顺序,不断选取当前未被选取的边集中权值最小的边。
依据生成树的概念,n 个结点的生成树,有n-1 条边,故反复上述过程,直到选取了n-1 条边为止,就构成了一棵最小生成树。
实验目的:
本实验通过实现最小生成树的算法,使学生理解图的数据结构存储表示,并能理解最小生成树Kruskal 算法。
通过练习,加强对算法的理解,提高编程能力。
实验内容:
(1)假定每对顶点表示图的一条边,每条边对应一个权值;
(2)输入每条边的顶点和权值;
(3)输入每条边后,计算出最小生成树;
(4)打印最小生成树边的顶点及权值。
实验器材(设备、元器件):
PC机一台,装有C语言集成开发环境。
数据结构与程序:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define X 105
typedef struct Edge
{
int w;
int x, y;
} Edge; //储存边的struct,并储存边两端的结点
class GraphNode
{
public:
int data;
int father;
int child;
} GraphNode[X]; //储存点信息的并查集类(点的值,父结点,子结点)Edge edge[X*X];
bool comp(const Edge, const Edge);
void update(int);
int main()
{
int node_num;
int sum_weight = 0;
FILE *in = fopen("C:\\Users\\瑞奇\\Desktop\\编程实验\\数据结构实验\\FileTemp\\in.txt", "r");
cout << "Reading data from file..." << endl << endl;
//cout << "Please input the total amount of nodes in this Graph: ";
//cin >> node_num;
fscanf(in, "%d", &node_num);
//cout << "Please input the data of each node: " << endl;
for(int i = 1;i <= node_num;i++)
{
//cin >> GraphNode[i].data;
fscanf(in, "%d", &GraphNode[i].data);
GraphNode[i].father = GraphNode[i].child = i;
} //初始化点集
//cout << "Please input the relation between nodes in this format and end with (0 0 0):" << endl << "(first_node second_node egde_weight)" << endl;
int x, y, w, tmp_cnt = 1;
//while(cin >> x >> y >> w && w)
while(fscanf(in, "%d%d%d", &x, &y, &w) != EOF && w)
edge[tmp_cnt].w = w, edge[tmp_cnt].x = x, edge[tmp_cnt++].y = y;
fclose(in);
sort(edge+1, edge+tmp_cnt, comp); //对边权进行排序
cout << "The MinSpanTree contains following edges: " << endl << endl;
for(int i = 1;i <= tmp_cnt;i++) //循环找最小边
if(GraphNode[edge[i].x].father != GraphNode[edge[i].y].father)
{
int n = edge[i].x;
int m = n;
if(GraphNode[m].father != m) //使用并查集对边是否可用进行判断
{
m = GraphNode[m].father;
GraphNode[m].father = GraphNode[edge[i].y].father;
}
GraphNode[edge[i].x].father = GraphNode[edge[i].y].father;
GraphNode[edge[i].y].child = GraphNode[edge[i].x].child;
while(GraphNode[n].child != n)
n = GraphNode[n].child;
update(n); //在合并点集后对并查集进行更新
sum_weight += edge[i].w; //计算总权
cout << "\t" << "The edge between " << GraphNode[edge[i].x].data << " & " << GraphNode[edge[i].y].data << " with the weight " << edge[i].w << endl;
}
cout << endl << "And the total weight of the MinSpanTree add up to: " << sum_weight << endl;
return 0;
}
bool comp(const Edge a, const Edge b)
{
return a.w < b.w;
}
void update(int n)
{
if(GraphNode[n].father == n)
return;
GraphNode[GraphNode[n].father].child = GraphNode[n].child;
//更新孩子结点update(GraphNode[n].father); //递归更新
GraphNode[n].father = GraphNode[GraphNode[n].father].father;
//更新父结点
}
程序运行结果:
运行程序,程序读取文件,获取文件中关于图的信息:结点数,结点值,结点间边权。
然后使用Kruskal算法对录入信息进行处理:
1.对边权排序
2.取最小权边,若边的端结点不在同一集合众,则使边的端结点加入集合并删除该边;若边的端结点本来就在同一集合中,直接删除该边
3.循环执行步骤2,直到集合中包含所有结点和结点数-1条边
输入为:
6
1 2 3 4 5 6
1 2 6
1 3 1
1 4 5
2 3 5
2 5 3
3 4 5
3 5 6
3 6 4
4 6 2
5 6 6
程序运行结果如下图:
实验结论:
Kruskal算法其实是一种贪心算法,每次选取符合条件的边,加入边集(此程序中直接输出)。
直到所有结点和最少边全部包含在同一集合中,算法结束。
总结及心得体会:
在使用并查集的时候,注意在合并集合后要更新并查集的父结点和子结点。
其实Kruskal算法的复杂度为O(E^2),其复杂度和边条数有关,和结点数无关,所以适用于稀疏图。