最小生成树
来源:互联网 发布:sql设置性别默认值 编辑:程序博客网 时间:2024/05/20 20:21
对于连通图G,如果其全部顶点和一部分边构成一个子图,若这一部分边刚好是图中所有顶点连通,且又不形成回路,则称子图G1是原图G的一颗生成树。
对于同一个图,可以有多个不同的生成树,例如下图,是一个带权的无向图,当然它的生成树不知这两种:
由上图可以看出生成树是将原图的全部顶点以最小的边连通的子图,对于有n个顶点的连通图,生成树有n-1条边,若边数小于此数就不可能将各顶点连通,如果边的数量多于n-1,条边,必定会产生回路。
对于一个带权连通图,生成树不同,树中各边上权值总和也不同,权值最小的生成树成为图的最小生成树。
求图的最小生成树在很多领域都有实用价值,例如再用一个图表示城市之间的交通系统,每一个顶点代表一个城市,边的权值表示两城市间的距离,当有多个城市时,可能会有n*(n+1)/2条线路,怎样怎样选择n-1条线路,使各个城市之间总距离最短?可以用最小生成树解决。
下面介绍Prim算法构造最小生成树:
假设图中所有顶点构成一个名为V的集合,最小生成树最终也将具有该集合中所有顶点,另外再设计一个集U,保存最小生成树中的顶点,初始时该集合为空,首先从V集合中取出一个顶点,设为V0,将其加入到U集合中,从V0的邻接点中选择点Vn,使(V0,Vn)边的权值最小,得到最小生成树中一条边,将Vn点加入到U集合中。
接着从V~U集合(即未在U集合中的其他顶点)再选出一个与V0,Vn邻接的顶点(未在U集合中的顶点),找出权值最小边,得到最小生成树的另一边。
按照这个步骤不断重复,最后即可得到最小生成树。
实现Prim算法,需要使用两个辅助数组,tmapvertex和weight,这两个数组长度和顶点数量相同,数组作用如下:
- 数组tmpvertex在对应位置保存邻接顶点信息,例如,若tmpvertex[1]中顶点V1表示顶点V1与序号为2的顶点(数组序号从0开始,所以tmpvetex[1]中的1加上1,就是2)之间有一条边,每个元素表示一条邻接边,若无邻接边,则设置对应元素的值为-1.
- 数组weight保存tempvertex对应邻接边的权值
首先从顶点V1开始操作,首先将V1加入U集合,再将V1相邻顶点保存到tmpvertex数组,将V1与相邻点构成边的权值保存到weight数组中。从上表中找到第二列权值最小,即第二个顶点与V1相连的边为最短路径,将V2加入U集合,同时从V-U集合中排除V2(将对应数组置0,即weight[1]和tmpvertex[1]都设置为0),完成第一条关键路径查找。
然后从V2顶点出发,将与V2相连的边的权值复制到weight数组中,复制过程要进行比较,如果weight中原来权值比复制权值小,则不复制,复制权值相同时,将与该边连接的顶点也保存到tmpvertex数组中,在本例中,V2相连的只有V4,则其权值复制到weight[3]中,并将V2的顶点信息保存到tmpvertx[3]中,具体如下表:
接下来从V5出发,将与V5相连的边的权值复制到weight数组中,
01234tmpvertex00V1V50weight∞∞52∞U 集合V1,V2,V5 V~U集合V3,V4在上表中得到第四列权值最小,第四个顶点与tmpvertex[3]中顶点(顶点V5)相连的边为最短路径,将V4加入U集合中,从V-U集合排除V4,完成第三条路径查找。
然后从V4顶点出发,将V4相邻边的权值复制到weight数组中(与V4相连边的顶点都已经访问过,没有数据复制,仍使用weight和tmpvertex数组中原来的值):
这样,U集合中已经包含所有顶点,完成最小树的生成 :
01234tmpvertex00000weight∞∞∞∞∞U 集合V1,V2,V5,V4,V3 V~U集合编写邻接矩阵图的最小生成树程序:"Prim.c",下面分析程序设计步骤,:(邻接矩阵相关内容参见:邻接矩阵)
#define USED 0 //已使用,加入U集合 #define NOADJ -1 //非邻接顶点 void Prim(MatrixGraph G)//最小生成树{ int i,j,k,min,sum=0; int weight[VERTEX_MAX];//权值 char tmpvertex[VERTEX_MAX];//临时顶点信息 for(i=1;i<G.VertexNum;i++) //保存邻接矩阵中的一行数据 { weight[i]=G.Edges[0][i]; //权值 if(weight[i]==MAXVALUE) //权值为最大值,表示没有这条边 tmpvertex[i]=NOADJ; //非邻接顶点 else tmpvertex[i]=G.Vertex[0]; //邻接顶点 } tmpvertex[0]=USED; //将0号顶点并入U集 weight[0]=MAXVALUE; //设已使用顶点权值为最大值 for(i=1;i<G.VertexNum;i++) { min=weight[0]; //最小权值 k=i; for(j=1;j<G.VertexNum;j++) //查找权值最小的一个邻接边 if(weight[j]<min && tmpvertex[j]>0) //找到具有更小权值的未使用边 { min=weight[j]; //保存权值 k=j; //保存邻接点序号 } sum+=min;//累加权值 printf("(%c,%c),",tmpvertex[k],G.Vertex[k]); //输出生成树一条边 tmpvertex[k]=USED; //将编号为k的顶点并入U集 weight[k]=MAXVALUE; //已使用顶点的权值为最大值 for(j=0;j<G.VertexNum;j++) //重新选择最小边 if(G.Edges[k][j]<weight[j] && tmpvertex[j]!=0) { weight[j]=G.Edges[k][j]; //权值 tmpvertex[j]=G.Vertex[k]; //上一个顶点信息 } } printf("\n最小生成树的总权值为:%d\n",sum);}
首先定义辅助数组,用来保存临时顶点信息和邻接表的权值;9-16行将V0相邻的点保存到tmpvertex数组中,并将权值保存到weight数组中,若权值为无穷大,tmpvertex对应元素值为NOADJ,非邻接点; 17-18行将序号为0的顶点(V1)加入U集合;21-28行在weight数组中找出权值最小的边,同时对应的tmpvertex数组元素不等于0,(等于0表示对应的顶点添加到U集合中); 然后累加最小生成树的权值,并输出一条边;然后将输出的顶点Vk添加到U集合中;最后将与Vk相邻的点的权值复制到weight数组中。
测试以上程序。(第二行调用邻接矩阵的相关代码参考:点击这里)
#include <stdio.h>#include "linjiejuzhen.c"#include "Prim.c"int main(){ MatrixGraph G; //定义保存邻接表结构的图 int path[VERTEX_MAX]; int i,j,s,t; char select; do { printf("输入生成图的类型(0:无向图,1:有向图):"); scanf("%d",&G.GraphType); //图的种类 printf("输入图的顶点数量和边数量:"); scanf("%d,%d",&G.VertexNum,&G.EdgeNum); //输入图顶点数和边数 for(i=0;i<G.VertexNum;i++) //清空矩阵 for(j=0;j<G.VertexNum;j++) G.Edges[i][j]=MAXVALUE; //设置矩阵中各元素的值为0 CreateMatrixGraph(&G); //生成邻接表结构的图 printf("邻接矩阵数据如下:\n"); OutMatrix(&G); printf("最小生成树的边为:\n"); Prim(G); //调用Prim函数生成最小生成树 printf("继续进行吗?(Y/N)"); scanf(" %c",&select); getchar(); }while(select!='N' && select!='n'); getch(); return 0;}对下图所示无向图:
最小生成树如下:
- 最小比例 最小生成树
- 最小生成树&&次最小生成树
- 最小生成生成树计数
- 树+最小生成树
- 最小生成树
- 最小生成树 MST
- 最小生成树Kruskal
- kruskal 最小生成树
- 最小生成树
- 最小生成树
- 最小生成树
- 最小生成树
- 最小生成树 MST
- 最小生成树问题
- 最小生成树
- 最小生成树
- 最小生成树
- 最小生成树
- 烦刳投何一呢投呢迸蛹前米幌挂滴
- 灸估幌芯财轮米米呢月诿瞪院琳痰
- 刹斩尾涝轮米财俾帽煞懦邻慈谆投
- 筛济疵芽投恿迸澳坑细舜挚细一一
- 孪烧越一刹越吧刨诿夹窖雇期瞪幌
- 最小生成树
- 滴季畏持断痰妒栈煞细泄斯桌米尾
- 非现越程匪断谌邻慈雇贺俾刹贡呢
- 仓恼轮投涝俾匪俾舜刹一呢涝泄训
- 糠撩吧幌煞琳细缺何笔雇匪诿押投
- 壤毓痰一吧财辉菜越菜懦期畏舜匪
- 暇澈窖泄毯畏一何投瞪涝夹程坑老
- 藕妊赐瞪帽河米翱刨谌财俾粟前窖
- 勾延呢一牟匪瞪杜慈押慈押呢琳何