计算机软件基础课程设计最小生成树最优通信网.docx
《计算机软件基础课程设计最小生成树最优通信网.docx》由会员分享,可在线阅读,更多相关《计算机软件基础课程设计最小生成树最优通信网.docx(11页珍藏版)》请在冰豆网上搜索。
计算机软件基础课程设计最小生成树最优通信网
计算机软件基础课程设计
题目:
最小生成树——最优通信网
摘要
最小生成树的定义:
若有一个连通的无向图G,有n个顶点,并且它的边是有权值的。
在G上构造生成树G’,使这n-1条边的权值之和在所有的生成树中最小。
要在n个城市间建立交通网,要考虑的问题如何在保证n点连通的前题下最节省经费?
上述问题即要使得生成树各边权值之各最小,即构造最小生成树的准则:
必须只使用该网络中的边来构造最小生成树;
必须使用且仅使用n-1条边来联接网络中的n个顶点;
不能使用产生回路的边。
建立最小生成树的方法通常有两种:
(Prim)算法和(Kruskal)算法
邻接表及其实现:
用邻接矩阵表示法存储图,占用的存储单元个数只与图中顶点的个数有关,而与边的数目无关。
一个含有n个顶点的图,如果其边数比n2少得多,那么它的邻接矩阵就会有很多空元素,浪费了存储空间。
目录(宋体三号,加粗)
最小生成树的定义----------------------------第5页
MST性质-------------------------------------第5页
Prim算法的基本思想和实现--------------------第6页
Kruskal算法的基本思想和实现-----------------第8页
邻接表的存储结构和建立程序------------------第11页
设计心得------------------------------------第12页
参考文献------------------------------------第14页
最小生成树的定义:
若有一个连通的无向图G,有n个顶点,并且它的边是有权值的。
在G上构造生成树G’,使这n-1条边的权值之和在所有的生成树中最小。
要在n个城市间建立交通网,要考虑的问题如何在保证n点连通的前题下最节省经费?
上述问题即要使得生成树各边权值之各最小,即构造最小生成树的准则:
必须只使用该网络中的边来构造最小生成树;
必须使用且仅使用n-1条边来联接网络中的n个顶点;
不能使用产生回路的边。
邻接表的存储结构
邻接表的存储结构
MST性质:
假设G=(V,E)是一个连通网,U是顶点集V的一个非空真子集,若(u,v)是满足uU,vV-U的边(称这种边为两栖边)且(u,v)在所有的两栖边中具有最小的权值(此时,称(u,v)为最小两栖边),则必存在一棵包含边(u,v)的最小生成树。
求MST的一般算法描述
求MST的一般算法可描述为:
针对图G,从空树T开始,往集合T中逐条选择并加入n-1条安全边(u,v),最终生成一棵含n-1条边的MST。
当一条边(u,v)加入T时,必须保证T∪{(u,v)}仍是MST的子集,我们将这样的边称为T的安全边。
(Prim)算法和(Kruskal)算法是两个利用MST性质构造最小生成树的算法。
最小生成树的普里姆算法:
普里姆算法的基本思想:
从连通网络G={V,E}中的某一顶点u0出发,选择与它关联的具有最小权值的边(u0,v),将其顶点加入到生成树的顶点集合U中。
以后每一步从一个顶点在U中,而另一个顶点不在U中的各条边中选择权值最小的边(u,v),把它的顶点加入到集合U中。
如此继续下去,直到网络中的所有顶点都加入到生成树顶点集合U中为止。
Prim算法的基本步骤如下:
(1)初始化:
U={u0},TREE={};
(2)如果U=V(G),则输出最小生成树T,并结束算法;
(3)在所有两栖边中找一条权最小的边(u,v)(若候选两栖边中的最小边不止一条,可任选其中的一条),将边(u,v)加入到边集TREE中,并将顶点v并入集合U中。
(4)由于新顶点的加入,U的状态发生变化,需要对U与V-U之间的两栖边进行调整。
(5)转步骤
(2)
普里姆算法构造最小生成树的过程
Prim算法实现:
1、连通图用邻接矩阵表示:
inti,j,k,w;
Vertexva,vb;
printf("输入顶点数和边数\n");
scanf("%d%d",&G.vexnum,&G.arcnum);
printf("输入%d个顶点的值(<%d个字符,以空格间隔):
\n",G.vexnum,maxname);
for(i=0;iscanf("%s",G.vexs[i]);
for(i=0;ifor(j=0;jG.arcs[i][j]=0x7fffffff;
printf("输入%d条边的顶点1顶点2权值:
\n",G.arcnum);
for(k=0;k{
scanf("%s%s%d%*c",va,vb,&w);
i=locate(G,va);
j=locate(G,vb);
G.arcs[i][j]=G.arcs[j][i]=w;
}
2、生成树
inti,j,k;
minsideclosedge;
k=locate(G,u);
for(j=0;j{
strcpy(closedge[j].adjvex,u);//将u赋给closedge[j].adjvex
closedge[j].lowcost=G.arcs[k][j];
}
closedge[k].lowcost=0;
printf("最小代价生成树的各条边为:
\n");
for(i=1;i{
k=minimum(closedge,G);
printf("(%s-%s)\n",closedge[k].adjvex,G.vexs[k]);
closedge[k].lowcost=0;
for(j=0;jif(G.arcs[k][j]{
strcpy(closedge[j].adjvex,G.vexs[k]);
closedge[j].lowcost=G.arcs[k][j];
}
}
3、算法关键一步:
求第k条具有最小权值的边
intminimum(minsidemina,GraphG)
{
inti=0,j,k,min;
while(!
mina[i].lowcost)
i++;
min=mina[i].lowcost;
k=i;
for(j=i+1;jif(mina[j].lowcost>0&&min>mina[j].lowcost)
{
min=mina[j].lowcost;
k=j;
}
returnk;
}
为实现prim算法需附设一个辅助数组closedge,以记录从U到V-U具有最小代价的边。
对于每个顶点Vi属于V-U,在辅助数组中存在一个相应分量closedge[i-1],它包括两个域,其中lowcost存储该边上的权。
typedefstruct
{
Vertexadjvex;//当前点
intlowcost;//权值
}minside[maxvertexnumber];
程序运行结果截图:
最小生成树的克鲁斯卡尔算法:
Kruskal算法基本思想:
为使生成树上边的权值之和最小,显然,其中每一条边的权值应该尽可能地小。
克鲁斯卡尔算法的做法就是:
先构造一个只含n个顶点的子图SG,然后从权值最小的边开始,若它的添加不使SG中产生回路,则在SG上加上这条边,如此重复,直至加上n-1条边为止。
克鲁斯卡尔算法构造最小生成树的过程
克鲁斯卡尔算法的实现:
(1)初始化;
(2)将所有的边按权值大小排序;
(3)遍历具有相互间具有共同顶点的边,若一条边的两顶点在同一连同分量上,则舍弃
(4)如果具有n-1条边,则输出最小生成树,并结束算法。
用冒泡法对所有边的权值进行排序:
voidsortorder(edge&minedge)
{
inti,j,temp;
for(i=0;ifor(j=0;jif(minedge[j].cost>minedge[j+1].cost)
{
temp=minedge[j].adjvex;
minedge[j].adjvex=minedge[j+1].adjvex;
minedge[j+1].adjvex=temp;
temp=minedge[j].endvex;
minedge[j].endvex=minedge[j+1].endvex;
minedge[j+1].endvex=temp;
temp=minedge[j].cost;
minedge[j].cost=minedge[j+1].cost;
minedge[j+1].cost=temp;
}
}
寻找最小生成树的路径:
voidmintree(vex&minvex,edge&minedge){
inti,j,count=0,k=0;
for(i=0;i{
if(k==vexnum-1)
break;
if(minvex[minedge[i].adjvex].sign==minvex[minedge[i].endvex].sign)
continue;
else
{
minedge[i].sign=1;//把已经包含在最小生成树里的节点序号记为1
for(j=0;jif(minvex[j].sign==minvex[minedge[i].endvex].sign)
minvex[j].sign=minvex[minedge[i].adjvex].sign;
k++;
}
}
}
输出最小生成树的各边:
voidprint(vexminvex,edgeminedge)
{inti;
printf("最小生成树如下\n");
for(i=0;iif(minedge[i].sign==1)
printf("%c%c%d\n",minvex[minedge[i].adjvex].vexname,minvex[minedge[i].endvex].vexname,minedge[i].cost);
}
程序运行结果截图:
邻接表的存储结构
无向图的邻接表
对于图G中的每个顶点vi,该方法把所有邻接于vi的顶点vj链成一个带头结点的单链表,这个单链表就称为顶点vi的邻接表。
单链表中的每个结点至少包含两个域,一个为邻接点域(adjvex),它指示与顶点vi邻接的顶点在图中的位序,另一个为链域(next),它指示与顶点vi邻接的下一个结点。
为了便于随机访问任一顶点的邻接表,可将所有头结点顺序存储在一个向量中就构成了图的邻接表存储。
最后将图的顶点数及边数等信息与邻接表放在一起来描述图的存储结构。
#include
#include
#definem20/*预定义图的最大顶点数*/
typedefchardatatype;/*顶点信息数据类型*/
typedefstructnode{/*边表结点*/
intadjvex;/*邻接点*/
structnode*next;
}edgenode;
typedefstructvnode{/*头结点类型*/
datatypevertex;/*顶点信息*/
edgenode*firstedge;/*邻接链表头指针*/
}vertexnode;
typedefstruct{/*邻接表类型*/
vertexnodeadjlist[m];/*存放头结点的顺序表*/
intn,e;/*图的顶点数与边数*/
}adjgraph;
voidcreateadjgraph(adjgraph*g)
{inti,j,k;
edgenode*s;
printf("Pleaseinputnande:
\n");
scanf("%d%d",&g->n,&g->e);/*输入顶点数与边数*/
getchar();
printf("Pleaseinput%dvertex:
",g->n);
for(i=0;in;i++)
{scanf(“%c”,&g->adjlist[i].vertex);/*读入顶点信息*/
g->adjlist[i].firstedge=NULL;/*边表置为空表*/
}
printf("Pleaseinput%dedges:
",g->e);
for(k=0;ke;k++)/*循环e次建立边表*/
{scanf("%d%d",&i,&j);/*输入无序对(i,j)*/
s=(edgenode*)malloc(sizeof(edgenode));
s->adjvex=j;/*邻接点序号为j*/
s->next=g->adjlist[i].firstedge;
g->adjlist[i].firstedge=s;
s=(edgenode*)malloc(sizeof(edgenode));
s->adjvex=i;/*邻接点序号为i*/
s->next=g->adjlist[j].firstedge;
g->adjlist[j].firstedge=s;
/*将新结点*s插入顶点vj的边表头部*/
}
}
参考文献
[1]孟彩霞.计算机软件基础[M].
陕西:
西安电子科技大学出版社,2003.
[2]严蔚敏,吴伟民.数据结构[M].北京:
清华大学出版社,2005.
[3]谭浩强.C程序设计(第二版)[M].北京:
清华大学出版社,1999.
[4]CSDN网,
[5]编程中国,
[6]编程入门网,