图论论文最小生成树算法城市高速公路问题中的应用.docx
《图论论文最小生成树算法城市高速公路问题中的应用.docx》由会员分享,可在线阅读,更多相关《图论论文最小生成树算法城市高速公路问题中的应用.docx(19页珍藏版)》请在冰豆网上搜索。
![图论论文最小生成树算法城市高速公路问题中的应用.docx](https://file1.bdocx.com/fileroot1/2022-11/23/03143219-8a59-482c-b826-29d4aeb8e1cc/03143219-8a59-482c-b826-29d4aeb8e1cc1.gif)
图论论文最小生成树算法城市高速公路问题中的应用
XXXX研究生堂下考试答卷
2012-2013学年第1学期
2012年12月18日
最小生成树在城市高速公路问题中的应用
摘要:
城市高速公路问题就是以最短高速路程连接一组城市的问题,在城市规划和建设中应用广泛。
本文以最小生成树在城市高速公路问题中的应用为例,利用最小生成树的三种算法的分析和研究,阐明了最小生成树在最优化方面的作用。
关键词:
城市高速公路问题Prim算法Kruskal算法简易算法
一引言
图论是数学的一个分支。
它以图为研究对象。
在图论的课程体系中,图结构是一种非常重要的非线性数据结构。
带权图的最小生成树尤其被广泛应用在解决工程技术及科学管理等各个领域的最优化问题中。
二背景知识
1图和树:
图论中的图是由若干给定的点及连接两点的线所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系,用点代表事物,用连接两点的线表示相应两个事物间具有这种关系。
树是五圈连通无向图,如果树T的节点数为n,那么树的边数为n-1。
2生成树:
连通图G上的一个子图,该子图连通,无回路且包含图G的所有节点,称为连通图的极小连通子图。
一个连通图可以有多棵不同的生成树。
3最小生成树:
对一个带权连通图,也有多可不同的生成树。
由于该图是带权图,各边的权值不一定相等,因此这些生成树的各边权值之和也不一定相同,其中权值最小的生成树被称为该带权连通图的最小生成树。
4高速公路问题:
假设有N个城市,第i个城市的位置笛卡尔坐标为(xi,yi),每条公路可以连接两个城市。
目前原有的公路有m条,但是不能实现所有城市之间的连通,因此需要继续修建公路,在费用最低的原则下,实现N个城市的连通,还需要修建哪些条公路。
由于修路的费用与公路的长短是成正比的,所以这个问题就可以转化成求修建哪几条公路能够实现所有城市的连通,同时满足所修公路总长最短。
三最小生成树的求解方法
构造最小生成树可以有多种算法。
大多数《图论》教材中介绍了其中的两种算法Prim算法和Kruskal算法,本文另介绍一种简易算法来实现最小生成树的构造。
1Prim算法
思想:
普里姆算法通过逐个往生成树上添加顶点来构造连通网的最小生成树。
算法具体步骤:
(1)开始:
选取连通网中的任意一个顶点添加到最小生成树中。
(2)重复执行以下操作:
1)连通网的顶点集合分成两个部分:
已经添加到最小生成树中的顶点集合和尚未添加到最小生成树中的顶点集合;
2)找出所有连通这两个集合中顶点的边;
3)从中选取一条权值最小的边添加到生成树中,同时将与这条边相连的顶点也添加到生成树中。
(3)结束:
所有的顶点都被添加到最小生成树中。
2Kruskal算法
思想:
通过逐个往生成树上添加边来构造连通网的最小生成树。
算法具体步骤:
(1)将连通网中的所有顶点添加到最小生成树中,构造一个森林;
(2)将各边按照权值从小到大排序;
(3)按照排好的顺序向生成树中添加不使森林中产生回路的边(若构成回路则不添加,继续考察下一条边)。
直至该森林变成一棵树为止。
3简易算法
思想:
通过逐个从连通网中删除边来构造最小生成树。
算法具体步骤:
(1)将连通网中各边按照权值从大到小排序;
(2)按照排好的顺序从连通网中删除权值最大的边,条件是使删除该边后的子图仍然保持连通(若删除后子图不连通则改边保留,继续删除下一条边)。
直至子图中任何一条边都不能删除(即删除任意一条边都会造成该子图不连通)为止。
4三种算法的比较
(1)普里姆算法:
主要是对顶点进行操作;采用邻接矩阵作为存储结构,在行过程中对连通网中的每一个顶点都考察到了,因此普里姆算法的时间复杂度为
(n为连通网中顶点的个数)。
普里姆算法适用于求边稠密的连通网的最小生成树。
(2)克鲁斯卡尔算法:
主要是对边进行操作,时间复杂度主要取决于对边按照权值进行排序的时间,边的个数为e,排序的时间复杂度可以做到O(eloge),因此算法的时间复杂度为O(eloge)。
克鲁斯卡尔算法适用于求边稀疏的连通网的最小生成树。
(3)简易算法:
主要是对边进行操作,时间复杂度主要取决于对边按照权值进行排序的时间,边的个数为e,排序的时间复杂度可以做到O(eloge),因此算法的时间复杂度为O(eloge)。
该算法适用于求边稀疏的连通网的最小生成树。
四应用
利用最小生成树来解决高速公路问题,将高速公路问题中的城市看做图中的顶点,城市之间修建的道路看做图中顶点之间的边,城市之间所修修建的公路的长度看做是图中个边上的权值。
这样我们就把高速公路问题转换成了求一个有向连通网的最小生成树问题。
此时假设城市个数为6,分别为a,b,c,d,e,f。
并设其对应城市之间的公路距离权值及初始状态的连通无向图如下所示:
边
(a,b)
(a,c)
(a,d)
(b,c)
(b,e)
(c,d)
(c,e)
(c,f)
(d,f)
(e,f)
权值
6
1
5
5
3
5
6
4
2
6
1简易算法来求解最小生成树
(1)实现步骤:
从权值最大的边开始进行删除(e,f),(c,e),(a,b),(a,d)被删除后都没有破坏连通性,所以这些边可以从图中删除,得下图:
当删除第五条边(b,c)时,造成了图的连通性的破坏,所以该条边不能被删除必须保留。
下图为最终构造好的最小生成树:
(2)算法实现:
1)连通网的存储结构
表示城市高速公路网络的无向连通网采用邻接矩阵的存储方式进行存储。
由于需要区分已经存在的公路和需要修建的公路,所以在每条边上增加一个标志位,同时为了给所有的边排序,因此单独建立一个表示边信息的结构体数组结构。
具体实现如下:
typedefcharTownType;typedeffloatRoadType;
typedefstruct{
intn;/*图的顶点个数*/
intm;/*图的边个数*/
TownTypetowns[MAXVEX];/*顶点信息*/
RoadTyperoads[MAXVEX][MAXVEX];/*边信息*/
}GraphMatrix;
typedefstruct{
intstart_town,stop_town;/*边的起点和终点*/
RoadTypeweight;/*边的权*/
enum{EXIST,UNEXIST}ex;/*区别已经建好的公路和未修建的公路*/
}Edge;
Edgemst[50];
2)判断图的连通性函数
判断无向图的的连通性有很多方法,这里采用的是通过对图进行深度优先搜索,统计遍历过的顶点个数,如果顶点个数比图中顶点个数少,说明该图不连通,相反说明该图是连通图。
具体实现如下:
voiddfsMatrix(GraphMatrixGM,inti,intn,int&visited[])
{intj;
printf“(%d”,i);
visited[i]=1;
for(intj=0;jif(GM[i][j]!
=0&&GM[i][j]!
=MaxValue
&&!
visited[j])
dfsMatrix(GM,j,n);
}
遍历结束后可以通过统计visited[]数组中的值为1的元素的个数v来确定访问过的结点个数,如果vGraphThrough(GraphMatrixGM)
{
for(i=0;idfsMatrix(GM,0,n,visited);
for(i=0;iif(visited[i]==1)num++;
if(num==n)return1;
elsereturn0;
}
3)对权值进行由大到小排序
由于整个算法的时间复杂度主要取决于排序算法的效率高低,因此在这里我们采用的是快速排序算法QSort(Edge&mst,inti,intj),排序的时间复杂度可以做到O(eloge)。
具体算法实现就不再赘述。
4)简易算法的主体部分
inteasy(GraphMatrix&GM,Edgemst[])
{
inti,j,num=0,th,weight;
for(i=0;i{
if(mst[i].ex==UNEXIST)
{
weight=GM.roads[start_town][stop_town];
GM.roads[start_town][stop_town]=0;
}
th=GraphThrough(GM);
if(th==1)continue;
elseGM.roads[start_town][stop_town]=weight;
}
2Prim算法求解最小生成树
(1)实现步骤:
从节点a开始,依次添加节点c,f,d,b,e,并依次添加对应的边,各个步骤如下图所示:
(2)算法实现:
#include
#include
#include
#defineINFINITY1000
#definemax_name50
#definemax_vertex_num50
typedefcharvertex[max_name];//顶点名字串
typedefintadjMatrix[max_vertex_num][max_vertex_num];//邻接距阵
typedefstruct
{vertexadjvex;//邻接矩阵
intlowcost;//权值
}close[max_vertex_num];//定义一个结构以便在后面closedge使用
typedefstruct//定义图
{
vertexvexs[max_vertex_num];//顶点集
adjMatrixarcs;//边
intvexnum,arcnum;//点个数,边个数
}MGraph;
intLocateVex(MGraphG,vertexu)//若G中存在顶点u,则返回该点在图中位置;否则返回其他信息;
{
inti;
for(i=0;iif(strcmp(u,G.vexs[i])==0)
returni;
return1;
}
voidCreateGraph(MGraph&G)
{
inti,j,k,w;
vertexv1,v2;
printf("输入无向图顶点数和边数:
\n");
scanf("%d%d",&G.vexnum,&G.arcnum);
printf("输入各顶点的值:
\n",G.vexnum);
for(i=0;iscanf("%s",&G.vexs[i]);
for(i=0;ifor(j=0;jG.arcs[i][j]=INFINITY;
printf("输入一条边依附的顶点及权值:
\n",G.arcnum);//输入一条边依附的顶点及权值
for(k=0;k{
scanf("%s%s%d",v1,v2,&w);
i=LocateVex(G,v1);//v1在图中位置
j=LocateVex(G,v2);//v2在图中位置
G.arcs[i][j]=G.arcs[j][i]=w;//置于对称弧
}
}
intminimum(closec,MGraphG)//求出下一个节点第k个顶点
{
inti=0,j,k,min;
min=INFINITY;
//初始化
k=-1;
for(j=0;j<=G.vexnum;j++)//求最小
if(c[j].lowcost0)
{
min=c[j].lowcost;
k=j;
}
returnk;
}
voidPRIM(MGraphG,vertexu)
{
inti,j,k=0;
closeclosedge;//一个结构
boolisbreak=false;
k=LocateVex(G,u);//u在图中位置返回G.vexs[i]中下标
for(j=0;j<=G.vexnum;++j)//辅助数组初始化closedge从O开始
{
if(j!
=k)//没有自己到自己的
closedge[k].lowcost=0;
strcpy(closedge[j].adjvex,u);
closedge[j].lowcost=G.arcs[k][j];//列
}
intflag[1000];
flag[0]=0;
intcount=1;
for(i=1;i{
k=minimum(closedge,G);
if(k==-1)
{
isbreak=true;
break;
}
printf("%s-%s%d\n",closedge[k].adjvex,G.vexs[k],G.arcs[k][LocateVex(G,closedge[k].adjvex)]);//输出生成树的边
closedge[k].lowcost=0;//第k个顶点并入U集
flag[count]=k;
count++;
for(j=0;jif(G.arcs[k][j]{
strcpy(closedge[j].adjvex,G.vexs[k]);
closedge[j].lowcost=G.arcs[k][j];
}
}
if(isbreak)
{
printf("此图不连通,无最小支撑树!
\n访问过的点为:
\n");
for(i=0;i{
printf("%s",G.vexs[flag[i]]);
}
printf("\n");
}
}
voidmain()
{
MGraphG;
CreateGraph(G);
printf("最小生成树的各条边为:
\n");
PRIM(G,G.vexs[0]);
}
3Kruskal算法求解最小生成树
(1)实现步骤:
依次添加边(a,c),(d,f),(b,e),(c,f),(b,c),并依次添加对应节点,各个步骤结果如下图:
(2)算法实现:
#include
#include
#defineM20
#defineMAX20
typedefstruct//构造边
{
intbegin;
intend;
intweight;//权值
}edge;
typedefstruct
{
intadj;
intweight;
}AdjMatrix[MAX][MAX];
typedefstruct
{
AdjMatrixarc;
intvexnum,arcnum;//顶点数和边数
}MGraph;
voidCreatGraph(MGraph*);//函数申明构造图
voidsort(edge*,MGraph*);//函数申明对边的排序
voidMiniSpanTree(MGraph*);//最小生成树
intFind(int*,int);
voidSwapn(edge*,int,int);//交换两条边的权值和它们的起点和终点
voidCreatGraph(MGraph*G)//构件图G
{
inti,j,n,m;
printf("请输入图的顶点数和边数:
");
scanf("%d%d",&G->vexnum,&G->arcnum);
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请输入边的起始点和终点:
");
scanf("%d%d",&n,&m);
while(n<0||n>G->vexnum||m<0||m>G->vexnum)
{
printf("\n");
printf("\n");
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);//输入权值
}
}
voidsort(edgeedges[],MGraph*G)//对权值进行排序
{
inti,j;
for(i=1;iarcnum;i++)
{
for(j=i+1;j<=G->arcnum;j++)
{
if(edges[i].weight>edges[j].weight)
{
Swapn(edges,i,j);
}
}
}
printf("\n");
printf("\n");
printf("\n");
printf("权从小到大排序之后为:
\n");
printf("◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆\n");
printf("起点终点权\n");
for(i=1;i<=G->arcnum;i++)
{
printf("<<%d%d>>%d\n",edges[i].begin,edges[i].end,edges[i].weight);
}
}
voidSwapn(edge*edges,inti,intj)//交换权值以及头和尾
{
inttemp;
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;
}
voidMiniSpanTree(MGraph*G)//生成最小生成树
{
inti,j,n,m;
intk=1;
intparent[M];
edgeedges[M];
for(i=1;ivexnum;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");
printf("\n");
printf("\n");
printf("\n");
printf("最小生成树为:
\n");
printf("==================================\n");
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);
}
}
printf("**********************************\n");
}
intFind(int*parent,intf)//找尾
{
while(parent[f]>0)
{
f=parent[f];
}
returnf;
}
intmain(void)//主函数
{
MGraph*G;
G=(MGraph*)malloc(sizeof(MGraph));
if(G==NULL)
{
printf("memoryallcationfailed,goodbye");
exit
(1);
}
CreatGraph(G);
MiniSpanTree(G);
system("pause");
return0;
}
五总结
本文在介绍了Prim算法,Kruskal算法的同时,介绍了一种新的简易算法用来求解最小生成树,并对它们进行了一定的比较。
最后以高速公路问题作为应用,分别给出了三种算法的运算步骤和算法实现。
结果表明,三种算法都可以求解出最小生成树。
参考文献
[1]RonaldLGraham,PavolHell.Onthehistory