最小生成树问题的算法实现及复杂度分析天津大学计算机科学与技术学院算法设计与分析.docx
《最小生成树问题的算法实现及复杂度分析天津大学计算机科学与技术学院算法设计与分析.docx》由会员分享,可在线阅读,更多相关《最小生成树问题的算法实现及复杂度分析天津大学计算机科学与技术学院算法设计与分析.docx(14页珍藏版)》请在冰豆网上搜索。
![最小生成树问题的算法实现及复杂度分析天津大学计算机科学与技术学院算法设计与分析.docx](https://file1.bdocx.com/fileroot1/2023-1/4/f942b33e-8c86-4419-a774-9116af53077f/f942b33e-8c86-4419-a774-9116af53077f1.gif)
最小生成树问题的算法实现及复杂度分析天津大学计算机科学与技术学院算法设计与分析
算法设计与分析课程设计报告
学院计算机科学与技术
专业计算机科学与技术
年级2011
姓名XXX
学号
2013年5月19日
题目:
最小生成树问题的算法实现及复杂度分析
摘要:
该程序操作简单,具有一定的应用性。
数据结构是计算机科学的算法理论基础和软件设计的技术基础,在计算机领域中有着举足轻重的作用,是计算机学科的核心课程。
而最小生成树算法是算法设计与分析中的重要算法,最小生成树也是最短路径算法。
最短路径的问题在现实生活中应用非常广泛,如邮递员送信、公路造价等问题。
本设计以VisualStudio2010作为开发平台,C/C++语言作为编程语言,以邻接矩阵作为存储结构,编程实现了最小生成树算法。
构造最小生成树有很多算法,本文主要介绍了图的概念、图的遍历,并分析了PRIM经典算法的算法思想,最后用这种经典算法实现了最小生成树的生成。
引言:
假设要在n个城市之间建立通信联络网,则连接n个城市只需要n-1条线路。
这时,自然会考虑这样一个问题,如何在节省费用的前提下建立这个通信网?
自然在每两个城市之间都可以设置一条线路,而这相应的就要付出较高的经济代价。
n个城市之间最多可以设置n(n-1)/2条线路,那么如何在这些可能的线路中选择n-1条使总的代价最小呢?
可以用连通网来表示n个城市以及n个城市之间可能设置的通信线路,其中网的顶点表示城市,边表示两个城市之间的线路,赋予边的权值表示相应的代价。
对于n个顶点的连通网可以建立许多不同的生成树,每一个生成树都可以是一个通信网。
现在要选择这样一棵生成树,也就是使总的代价最小。
这个问题便是构造连通网的最小代价生成树(简称最小生成树)的问题。
最小生成树是指在所有生成树中,边上权值之和最小的生成树,另外最小生成树也可能是多个,他们之间的权值之和相等。
一棵生成树的代价就是树上各边的代价之和。
而实现这个运算的经典算法就是普利姆算法。
正文
普里姆(Prim)算法思想
普里姆算法则从另一个角度构造连通网的最小生成树。
它的基本思想是:
首先选取图中任意一个顶点v作为生成树的根,之后继续往生成树中添加顶点w,则在顶点w和顶点v之间必须有边,且该边上的权值应在所有和v相邻接的边中属最小。
在一般情况下,假设图G=(V,E)中已落在生成树上的顶点集为U,则尚未落在生成树上的顶点集为V-U,则从(V-U)顶点集中选取加入生成树的顶点w应满足下列条件:
它和生成树上的顶点之间的边上的权值是在联接这两类顶点的所有边中权值属最小。
从上述生成树的构造过程中回还可以发现一点,即每个顶点都是通过"一条边"加入到生成树上的,因此对集合V-U中的每个顶点,当它和集合U中的顶点有一条以上的边相连时,只需要保留一条权值最小的边即可。
由此,在普里姆算法中需要附设一个辅助数组closedge,以记录从集合U到集合V-U中每个顶点当前的权值最小边。
普里姆算法构造最小生成树的过程
普里姆(Prim)算法设计:
一:
定义模块:
1.头文件、新类型及固定值定义。
本程序可接受的最大顶点数为20个,没有连接的点之间,用100表示其权值。
#include
usingnamespacestd;
#defineMAX_VERTEX_NUM20//最大顶点数
#defineQM100//最大值
#defineOK1
#defineERROR0
intvisited[MAX_VERTEX_NUM];
typedefintVertexType;
typedefintVRType;
2.首先设计图的存储模块,即定义类型。
本程序采用的图定义是无向图的定义方式,存储模块采用邻接矩阵,便于查找。
typedefintVertexType;
typedefintVRType;
typedefstructArcCell//邻接矩阵的值
{
intadj;
}ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedefstruct
{
VertexTypevexs[MAX_VERTEX_NUM];//顶点向量
AdjMatrixarcs;//邻接矩阵
intvexnum,arcnum;//图的当前顶点数和弧数
}MGraph;
3.在实现最小生成树算法时,定义辅助数组,进行判断遍历。
typedefstruct
{
VertexTypeadjvex;
VRTypelowcost;
}closedge[MAX_VERTEX_NUM];
4.最后生成最小生成树时,采用辅助数组进行结果的记录。
typedefstruct
{
VertexTypehead;
VRTypelast;
intweight;
}result[MAX_VERTEX_NUM];
二:
实现模块(函数)
1.顶点查找函数,因为顶点的值和数组位置的下标不一定相等,所以加入顶点查找函数,返回顶点的值,便于结果显示、区分指针与内容,使思路更为清晰。
intLocateVex(MGraph&G,VertexTypev)//确定顶点位置
{
intk;
for(k=0;k{
if(G.vexs[k]==v)
returnk;
}
return-1;//没有这个顶点
}
2.无向图的创建函数。
intCreateUDN(MGraph&G)//创建无向图
{
inti,j,k;
intweight;
VertexTypev1,v2;
cout<<"输入无向图的顶点数和弧数:
"<cin>>G.vexnum>>G.arcnum;
for(i=0;ifor(j=0;jG.arcs[i][j].adj=QM;
cout<<"输出网的"<"<for(i=0;icin>>G.vexs[i];
cout<<"建立弧,请输入"<"<for(k=0;k{
cin>>v1>>v2>>weight;
i=LocateVex(G,v1);
j=LocateVex(G,v2);
if(i<0||j<0)
returnERROR;
G.arcs[i][j].adj=weight;
G.arcs[j][i].adj=G.arcs[i][j].adj;
}
returnOK;
}
3.最小生成树建立主程序,采用借助辅助数组的方式,对于辅助的数组,以邻接表的选择点加入该数组,然后查找数组中权值最小,且未被选中的顶点,然后返回该边,加入最小生成树中。
voidMiniSpanTree_PRIM(MGraphG,VertexTypeu)
{
closedgedge;//申请数组
resultpax;
intk,j,i,lax,time=0,x,y;
lax=G.vexnum-1;
k=LocateVex(G,u);//确定初始结点的下标
for(j=0;jif(j!
=k)
{
dge[j].lowcost=G.arcs[k][j].adj;//数组中的adjvex均为u,即从u开始到所有其他结点的权值赋给了数组中lowcost
dge[j].adjvex=u;//数组中的下标起箭头的作用,即它是边的第二个尾结点!
}
dge[k].lowcost=0;//u在数组dge的下标即为k,故自身到自身权值标为0,也表示纳入点集V!
for(i=1;i{
k=minimun(G,dge);//在dge数组中找到最小的一条边,并返回尾结点的下标!
cout<<"K的寻求结果为(数组dge中的下标):
"<cout<pax[time].head=dge[k].adjvex;
pax[time].last=G.vexs[k];
x=LocateVex(G,dge[k].adjvex);
y=LocateVex(G,G.vexs[k]);
pax[time].weight=G.arcs[x][y].adj;
time++;
dge[k].lowcost=0;//把下标为k的结点纳入点集V!
标注权值为0!
for(j=0;jif(G.arcs[k][j].adj{//新加入的点到其他各点的权值比原来的权值更小,则替换!
采取遍历的方法!
dge[j].lowcost=G.arcs[k][j].adj;
dge[j].adjvex=G.vexs[k];//点的名字进行修改!
}
}
cout<<"最小生成树为:
"<for(time=0;timecout<<""<}
4.辅助生成最小生成树的函数。
intminimun(MGraphG,closedgeF)
{
inti,min;
for(i=0;iif(F[i].lowcost!
=0)break;
min=i;
for(i=0;iif(F[i].lowcost!
=0&&F[i].lowcostmin=i;
returnmin;
}
数组的样式:
j
Lowcost
adjvex
过程如下表:
顶点标号都比图中的小1,比如v1为0,v2为1,这里首先选择v1点。
j
0
1
2
3
4
5
Lowcost
0
4
2
3
100
100
adjvex
1
1
1
1
1
1
从这个表格可以看到依附到v1顶点的v3的Lowcost最小为2,那么选择v3,选择了之后我们必须要更新Lowcost数组的值,因为记录从U到V-U具有最小代价的边,加入之后就会改变。
新加入的点到其他各点的权值比原来的权值更小,则替换!
采取遍历的方法!
j
0
1
2
3
4
5
Lowcost
0
4
0
1
100
2
adjvex
1
1
1
4
1
6
Lowcost=0为我们已经选出来的顶点,接着继续在Lowcost中选出值不为0的最小值,作为下一个最小生成树的点。
这样一直选择下去直到选出所有的顶点。
5.最小生成树建立,那么需要借用辅助数组,进行记录。
intminimun(MGraphG,closedgeF)
{
inti,min;
for(i=0;iif(F[i].lowcost!
=0)break;
min=i;
for(i=0;iif(F[i].lowcost!
=0&&F[i].lowcostmin=i;
returnmin;
}
6.调试时,加入的函数,输出邻接矩阵和辅助数组,进行查看和判断正误。
voidPrintMatrix(MGraph&G)//输出邻接矩阵
{
inti,j;
printf("邻接矩阵为:
\n");
for(i=0;i{
for(j=0;j{
if(G.arcs[i][j].adj!
=QM)
cout<elsecout<<"∞"<<"";
}
cout<}
}
voidprintclosedge(MGraphG,closedgeX)//输出辅助建立最小生成树数组
{
inti;
cout<<"dge数组值为:
"<";
for(i=0;icout<<""<
cout<";
for(i=0;icout<<""<cout<";
for(i=0;icout<<""<}
7.主程序。
voidmain()
{
MGraphG;
VertexTypeu;
G.vexnum=G.arcnum=0;
CreateUDN(G);
PrintMatrix(G);
cout<<"请输入要开始的点:
";
cin>>u;
MiniSpanTree_PRIM(G,u);
system("pause");
}
普里姆(Prim)算法分析:
Prim算法的时间复杂度主要是在双重循环构造最小生成树的过程中,设图的顶点数为n,则双重循环的时间复杂度为O(n2),在生成最小生成树的过程中,增加了两个数组,closedge[]和result[]数组,用来记录所选顶点的全趋结点,故空间复杂度为O(2n)。
普里姆算法的时间复杂度与边数e无关,该算法更适合于求边数较多的带权无向连通图的最小生成树。
普里姆(Prim)算法实验结果:
采用的数据:
611
123456
124
143
132
253
244
235
341
362
456
462
564
1
实验结果:
图表分析:
2
42
3
5
41
32
2
6
4
(a)(b)
2
2
11
1
2
(c)(d)
4
42
2
1
1
232
(e)(f)
结论与展望:
运用prim算法构造图的最小生成树,使生成树的权值和达到最小,即耗费最小,这里选择贪心策略,从一个顶点出发,选择到剩余顶点的边权值最小的顶点,将之并入到所够造的生成树之中,同时修改剩下的顶点到生成树的权值,再从剩余顶点中继续选择到生成树耗费最小的顶点,继续并入该顶点,直到所有的顶点全部并入到生成树中为止,其核心思想在于不断地在剩余顶点中选取到生成树耗费最小的顶点,将之并入后,修改剩余顶点到生成树相应的权值,体会到了贪心思想,实验完成较为成功。
除此之外,通过本次课程设计巩固了课本的基本知识,熟练运用课程知识。
提高我们组织数据及编写程序的能力,使我们能够根据问题要求和数据对象的特性,学会数据组织的方法,把现实世界中的问题在计算机内部表示出来并用软件解决问题,本次实验大大提高了我对编程的爱好。
但对算法的时间复杂度感到略微的不足,希望将来能加以改进,实现更为简练的最小生成树算法。