普里姆算法求最小生成树.docx
《普里姆算法求最小生成树.docx》由会员分享,可在线阅读,更多相关《普里姆算法求最小生成树.docx(25页珍藏版)》请在冰豆网上搜索。
普里姆算法求最小生成树
沈阳航空航天大学
课程设计报告
课程设计名称:
数据结构课程设计
课程设计题目:
Prim算法求最小生成树
院(系):
计算机学院
专业:
计算机科学与技术(物联网方向)
班级:
学号:
姓名:
指导教师:
学术诚信声明
本人声明:
所呈交的报告(含电子版及数据文件)是我个人在导师指导下独立进行设计工作及取得的研究结果。
尽我所知,除了文中特别加以标注或致谢中所罗列的内容以外,报告中不包含其他人己经发表或撰写过的研究结果,也不包含其它教育机构使用过的材料。
与我一同工作的同学对本研究所做的任何贡献均己在报告中做了明确的说明并表示了谢意。
报告资料及实验数据若有不实之处,本人愿意接受本教学环节“不及格”和“重修或重做”的评分结论并承担相关一切后果。
本人签名:
日期:
2015年1月15日
沈阳航空航天大学
课程设计任务书
课程设计名称
数据结构课程设计
专业
计算机科学与技术(物联网方向)
学生姓名
班级
学号
题目名称
Prim算法生成最小生成树
起止日期
2015
年
1
月
5
日起至
2015
年
1
月
16
日止
课设内容和要求:
在n个城市之间建立网络,只需保证连通即可,求最经济的架设方法,利用Prim算法输出n个城市之间网络图,输出n个节点的最小生成树。
其中,n个城市表示n个节点,两个城市间如果有路则用边连接,生成一个n个节点的边权树,要求键盘输入。
参考资料:
算法与数据结构,严蔚敏、 吴伟民,清华大学出版社,2006
C程序设计,谭浩强,清华大学出版社,2010
教研室审核意见:
教研室主任签字:
指导教师(签名)
年
月
日
学生(签名)
2015
年
1
月15
日
一课程设计目的和要求
1.1课程设计目的
(一)根据算法设计需要,掌握连通网的数据表示方法;
(二)
(三)掌握最小生成树的Prim算法;
(四)
(五)学习独立撰写报告文档。
(六)
1.2课程设计的要求
在n个城市之间建立网络,只需保证连通即可,求最经济的架设方法,利用Prim算法输出n个城市之间网络图,输出n个节点的最小生成树。
其中,n个城市表示n个节点,两个城市间如果有路则用边连接,生成一个n个节点的边权树,要求键盘输入。
二实验原理分析
2.1最小生成树的定义
一个有n个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有n个结点,并且有保持图连通的最少的边。
最小生成树可以用kruskal(克鲁斯卡尔)算法或Prim(普里姆)算法求出。
(1).最小生成树的概述
(2).
在一给定的无向图G=(V,E)中,(u,v)代表连接顶点u与顶点v的边(即),而w(u,v)代表此边的权重,若存在T为E的子集(即)且为无循环图,使得w(T)最小,则此T为G的最小生成树。
最小生成树其实是最小权重生成树的简称。
(3).最小生成树的分析
(4).
构造最小生成树可以用多种算法。
其中多数算法利用了最小生成树的下面一种简称为MST的性质:
假设N=(V,{E})是一个连通网,U是顶点集V的一个非空子集。
若(u,v)是一条具有最小权值(代价)的边,其中u∈U,v∈V-U,则必存在一棵包含边(u.v)的最小生成树。
2.2Prim算法的基本思想
假设G=(V,E)是一个具有n个顶点的连通网,T=(U,TE)是G的最小生成树,其中U是T的顶点集,TE是T的边集,U和TE的初值均为空集。
算法开始时,首先从V中任取一个顶点(假定取V0),将它并入U中,此时U={V0},然后只要U是V的真子集,就从那些其一个端点已在T中,另一个端点仍在T外的所有边中,找一条最短(即权值最小)边,假定为(i,j),其中Vi∈U,Vj∈(V-U),并把该边(i,j)和顶点j分别并入T的边集TE和顶点集U,如此进行下去,每次往生成树里并入一个顶点和一条边,直到n-1次后就把所有n个顶点都并入到生成树T的顶点集中,此时U=V,TE中含有n-1条边,T就是最后得到的最小生成树。
可以看出,在普利姆算法中,是采用逐步增加U中的顶点,常称为“加点法”。
为了实现这个算法在本设计中需要设置一个辅助数组closedge[],以记录从U到V-U具有最小代价的边。
当辅助数组中存在两个或两个以上的最小代价的边时,此时最小生成树的形态就不唯一,此时可以在程序中采用递归的算法思想求出每个最小生成树。
(1).在prim算法中要解决两个问题
(2).
1)在无向网中,当从一个顶点到其他顶点时,若其边上的权值相等,则可能从某一起点出发时,会得到不同的生成树,但最小生成树的权值必定相等,此时我们应该如何把所有的最小生成树求解出来;
2)
3)每次如何从生成树T中到T外的所有边中,找出一条权值最小的边。
例如,在第k次(1≤k≤n-1)前,生成树T中已有k个顶点和k-1条边,此时T中到T外的所有边数为k(n-k),当然它也包括两顶点间没有直接边相连,其权值被看作常量的边在内,从如此多的边中找出最短的边,其时间复杂度0(k(n-k)),是很费时间的,是否有好的方法能够降低查找最短边的时间复杂度。
4)
(3).上述问题的解决方法
(4).
针对1)中出现的问题,可以通过算法来实现,详情请看Prim算法的概述;
针对2)中出现的问题,通过对Prim算法的分析,可以使查找最短边的时间复杂度降低到O(n-k)。
具体方法是假定在进行第k次前已经保留着从T中到T外的每一顶点(共n-k个顶点)的各一条最短边,进行第k次时,首先从这n-k条最短边中,找出一条最最短的边,它就是从T中到T外的所有边中的最短边,假设为(i,j),此步需进行n-k次比较;然后把边(i,j)和顶点j分别并入T中的边集TE和顶点集U中,此时T外只有n-(k+1)个顶点,对于其中的每个顶点t,若(j,t)边上的权值小于已保留的从原T中到顶点t的最短边的权值,则用(j,t)修改之,使从T中到T外顶点t的最短边为(j,t),否则原有最短边保持不变,这样,就把第k次后从T中到T外每一顶点t的各一条最短边都保留下来了,为进行第k+1次运算做好了准备,此步需进行n-k-1次比较。
所以,利用此方法求第k次的最短边共需比较2(n-k)-1次,即时间复杂度为O(n-k)。
三概要分析和设计
3.1概要分析
通过对上述算法的分析,将从以下三方面来进行分析:
(1).输入数据的类型
(2).
在本次设计中,是对无向图进行操作,网中的顶点数,边数,顶点的编号及每条边的权值都是通过键盘输入的,他们的数据类型均为整型,其中,权值的范围为0~32768(即“∞”);
(3).输出数据的类型
(4).
当用户将无向图创建成功以后,就可以通过键盘任意输入一个起点值将其对应的最小生成树的生成路径及其权值显示出来;
(5).测试数据
(6).
本次设计中是通过用Prim算法求最小生成树,分别用以下三组数据进行测试:
(一)假设在创建无向图时,只输入一个顶点,如图1所示,验证运行结果;
(二)
A
图1.单一节点的无向图
(三)假设创建的无向图如图2所示,验证运行结果;
(四)
图2.网中存在权值相等的边
(五)假设创建的无向图如图3所示,验证结果;
(六)
图3,网中的权值各不相等
3.2概要设计
在本次设计中,网的定义为G=(V,E),V表示非空顶点集,E表示边集,其存储结构这里采用邻接矩阵的方式来存储。
1数据类型的定义在本设计中所涉及到的数据类型定义如下:
(1).符号常量的定义
(2).
算法中涉及到两个符号常量,定义如下:
#defineMAX20功能:
用于表示网中最多顶点个数;
#defineINFINIT32768功能:
用于表示权的最大值,即∞。
(3).结构体的定义
(4).
整个算法中涉及的结构体定义如下:
✓定义结构体ArcNode
✓
功能:
用于存放边的权值
typedefstruct
{
intadj;//权值
}ArcNode;
✓定义结构体AdjMatrix
✓
功能:
用于表示网的结构及存储方式。
typedef struct
{int vexs[MAX];//vexs表示顶点向量
int vexnum,arcnum;//分别表示图的顶点数和弧数
ArcNode arcs[MAX][MAX];//邻接矩阵
}AdjMatrix
✓定义结构体Node
✓
功能:
用于表示求最小生成树时用到的辅助数组。
typedefstruct
{intadjvex;//存放顶点编号
intlowcost;//存放顶点权值
}Node;
✓外部变量的定义
✓
算法中涉及到两个外部变量,定义如下:
Nodeclose[MAX]
功能:
存放每个顶点所连接的边所对应的权值;
intflag=0
功能:
标志变量,当创建网时,输入的顶点个数<=1时,直接输出“不存在最小生成树”程序不在往后执行,否则继续往下执行。
(5).函数模块
(6).
在本设计中一共包括六个模块:
一、子函数intLocate(AdjMatrix*G,intV)
二、
功能:
是求出某个顶点在顶点向量表中的位置,在其函数体中通过for循环将某一顶点与顶点向量表中的所有顶点进行比较,当出现两者相等时,将该顶点在vexs[MAX]数组中的下标通过return语句返回,否则返回-1;
三、子函数AdjMatrix*creat(AdjMatrix*G)
四、
功能:
是完成无向网的创建,在其函数体中,首先通过键盘输入网中顶点数,若顶点个数<=1时,将标志变量flag置为1并显示“最小生成树不存在”,然后返回主调函数;否则,继续通过键盘输入网中的边数,通过二重for循环初始化邻接矩阵,然后输入各个顶点的编号及每条边的权值,调用函数Locate()求出每条边所对应的两个顶点在顶点向量表中的位置后,将对应在邻接矩阵中的相应位置赋予权值,从而在邻接矩阵中存放了相关连的顶点之间边的权值,完成无向网的存储;
五、子函数intMinium(AdjMatrix*G,Nodeclose[])
六、
功能:
是求出辅助数组close[]中权值最小的边,在其函数体中,首先将最小权值(min)置为INFINIT(∞),通过for循环将辅助数组中的各条边的权值与min进行比较,最终使得min中存放的是当前数组close[]中最小的权值,并将其在辅助数组中的相应位置返回主调函数中;
七、子函数voidprim(AdjMatrix*G,intu)
八、
功能:
是利用PRIM(普里姆)算法求出无向网所对应的最小生成树,在其函数体中,首先,定义AdjMatrix*p用于存放最小生成树中的顶点(按生成最小生成树时的顺序存放),调用函数Locate()求出起点u在顶点向量表中的位置,将u存放到p的顶点向量表中,初始化初始化U={u},利用for循环对V-U中的顶点i,初始化close[i],再利用for循环找n-1条边(n=G->vexnum),其中,调用函数Minium()求出辅助数组close[]中权值最小的边,并将其在辅助数组中的相应位置返回到主调函数中并赋给k0,此时close[k0]中存有当前最小边(u0,v0)的信息,将边所对应的终点放入p的顶点向量表中,累加边的权值;然后,输出<起点终点权值>;最后,在顶点v0并入U之后,利用for循环更新closedge[i];当所有的顶点v0都纳入U集合之后,输出最小生成树中的顶点序列及最小生成树的权值之和。
九、子函数voiddisplay(AdjMatrix*G)
十、
功能:
是创建的无向网所对应的邻接矩阵;
十一、主函数voidmain()
十二、
功能:
是完成对上述各子函数的调用,完成本次设计的要求,在其函数体中,通过while循环可以实现重复创建网以及可以从网中的不同顶点出发求出对应的最小生成树。
(7).流程图
(8).
(9).
开始
输入ch,用于判断是否创建无向网
Ch=“Y”
结束
输入起点st
1.调用create()函数
2.调用display()函数
Flog=0
st=0
3.调用prim()函数
4.调用minium()函数
图4.流程图
上述流程图反映了整个算法中各个子函数之间的嵌套调用,从主函数开始顺序往下执行,首先调用creat()函数创建无向网并采用邻接矩阵的方式来存储;然后将该网对应的邻接矩阵通过调用display()函数输出;最后调用prim()函数求出该网所对应的最小生成树,并且在prim()函数中通过嵌套调用Minium()函数求出辅助数组close[]中权值最小的边,从而完成了本设计的要求。
四测试结果
该部分是对前面任务定义中的测试数据进行验证和分析:
4.1实验一
4.2
只含一个顶点的无向图。
图5.只含一个顶点的无向图
4.3实验二
4.4
假定创建的无向网的顶点个数>1,并且网中有些边的权值相等。
图7.运行结果
分析:
在上述创建的无向网中,有些顶点之间没有边相连接,所以在邻接矩阵中用∞表示,由于是以顶点1作为起点生成最小生成树,所以上述运行结果对应的生成树如图7所示。
4.3实验三
假定创建的无向网的顶点个数>1,并且网中有些边的权值不相等。
运行结果如下:
参考文献
(1).吴玉蓉,数据结构(C语言版),中国水利水电出版社,2008年;
(2).
(3).徐孝凯,数据结构实用教程,清华大学出版社,2005年7月;
(4).
(5).谭浩强,C语言程序设计教程,高等教育出版社,2004年5月。
(6).
附录(关键部分程序清单)
#include"stdio.h"
#include"string.h"
#include"malloc.h"
#include"iostream.h"
#include"iomanip.h"
#defineMAX20//最多顶点个数
#defineINFINIT32768//表示极大值,即∞
typedefstruct
{
intadj;//adj是权值类型
}ArcNode;
typedefstruct
{
intvexs[MAX],vexnum,arcnum;/*vexs表示顶点向量;vexnum,arcnum分别表示图的顶点数和弧数*/
ArcNodearcs[MAX][MAX];/*邻接矩阵*/
}AdjMatrix;
typedefstruct
{
intadjvex;//存放顶点编号
intlowcost;//存放顶点权值
}Node;
Nodeclose[MAX];//求最小生成树时的辅助数组
intflag=0;//功能:
标志变量,当创建网时,输入的顶点个数<=1时,直接输出“不存在最小生成树”程序不在往后执行,否则继续往下执行
intLocate(AdjMatrix*G,intV)//求顶点位置函数
{
intj=-1,k;
for(k=0;kvexnum;k++)
if(G->vexs[k]==V)
{
j=k;
break;
}
returnj;
}
AdjMatrix*creat(AdjMatrix*G)//创建无向网
{
inti,j,k,v1,v2,weight,m=1;
printf("请输入网中的顶点数:
");
scanf("%d",&G->vexnum);
if(G->vexnum<=1)
{
printf("\n****************最小生成树不存在!
*********************\n");
flag=1;
returnG;
}
else
{
printf("请输入网中的边数:
");
scanf("%d",&G->arcnum);
for(i=0;ivexnum;i++)//初始化邻接矩阵
for(j=0;jvexnum;j++)
if(i==j)
G->arcs[i][j].adj=0;
else
G->arcs[i][j].adj=INFINIT;
printf("请输入网中的顶点编号:
");//输入网中的顶点编号
for(i=0;ivexnum;i++)
scanf("%d",&G->vexs[i]);
printf("输入每条弧所对应的两个顶点及权值<格式:
起点终点权值>!
\n");
for(k=0;karcnum;k++)
{
printf("请输入第%d条边:
",m);
m++;
scanf("%d%d%d",&v1,&v2,&weight);//输入一条弧的两个顶点及权值
i=Locate(G,v1);
j=Locate(G,v2);
G->arcs[i][j].adj=weight;
G->arcs[j][i].adj=weight;
}
return(G);
}
}
intMinium(AdjMatrix*G,Nodeclose[])//close[]中权值最小的边
{
inti,min,k;
min=INFINIT;//置最小权值为INFINIT
for(i=0;ivexnum;i++)
if(close[i].lowcost=0)
{
min=close[i].lowcost;
k=i;
}
returnk;//返回权值最小的边在辅助数组中的位置
}
voidprim(AdjMatrix*G,intu)//普里姆算法//从顶点u出发,按普里姆算法构造连通网G的最小生成树,并输出生成树的每条边
{
inti,j,k,k0,u0,v0,s=0,n=0;
AdjMatrix*p;
p=(AdjMatrix*)malloc(sizeof(AdjMatrix));
k=Locate(G,u);//k为顶点u的位置
p->vexs[n++]=u;
close[k].lowcost=0;//初始化U={u}
for(i=0;ivexnum;i++)
if(i!
=k)//对V-U中的顶点i,初始化close[i]
{
close[i].adjvex=u;
close[i].lowcost=G->arcs[k][i].adj;
}
for(j=1;j<=G->vexnum-1;j++)//n-1条边(n=G->vexnum)
{
k0=Minium(G,close);//close[k0]中存有当前最小边(u0,v0)的信息
u0=close[k0].lowcost;//u0∈U
v0=G->vexs[k0];//v0∈V-U
p->vexs[n++]=v0;//将终点放入数组中
s+=close[k0].lowcost;//求最小生成树的权值之和
printf("<%d->%d\t%d>\n",u,v0,close[k0].lowcost);//输出最小生成树的路径
close[k0].lowcost=0;//将顶点v0纳入U集合
for(i=0;ivexnum;i++)//在顶点v0并入U之后,更新closedge[i]
if(G->arcs[k0][i].adj{
close[i].lowcost=G->arcs[k0][i].adj;
close[i].adjvex=v0;
}
}
printf("\n最小生成树中的顶点序列为:
[");
for(i=0;ivexnum;i++)
printf("%d",p->vexs[i]);
printf("]\n");
printf("\n最小生成树的权值之和为:
%d\n",s);
}
voiddisplay(AdjMatrix*G)//输出邻接矩阵算法
{
inti,j;
for(i=0;ivexnum;i++)
printf("\t%d",G->vexs[i]);
printf("\n");
printf("──│");
for(i=0;ivexnum;i++)
printf("────");
printf("\n");;
for(i=0;ivexnum;i++)
{
printf("%d│",G->vexs[i]);
for(j=0;jvexnum;j++)
if(G->arcs[i][j].adj==INFINIT)
printf("\t∞");
else
printf("\t%d",G->arcs[i][j].adj);
printf("\n");
}
for(i=0;ivexnum;i++)
printf("─────");
printf("\n");
}
voidmain()//主函数
{
charch;
intst;
AdjMatrix*G,*p;
p=(AdjMatrix*)malloc(sizeof(AdjMatrix));
printf("********************************************************************************\n");
printf("普里姆最小生成树算法!
\n");
printf("\n********************************************************************************\n");
printf("设计者:
李浩渊\n");
printf("班级:
34010105