图的最小生成树

来源:互联网 发布:正版办公软件 编辑:程序博客网 时间:2024/06/05 23:08

首先回顾一下概念

        无向图中连通且n个顶点n-1条边叫生成树有向图中一顶点入度为0其余顶点入度为1的叫有向树。一个连通图的生成树是一个极小的连通子图,它含有图中的全部顶点,但只有足以构成一棵树的n-1条边。我们把构造连通网的最小代价生成树称为最小生成树

        寻找连通网的最小生成树,经典算法有两种,Prim(普里姆)算法和Kruskal(克鲁斯卡尔)算法。

        1、Prim算法

        Prim算法的具体过程如下:假设N={V,{E}}是连通网,TE是N的最小生成树中边的集合。算法从最小生成树的顶点U={u0}(u0∈V),TE={}开始。重复执行下述操作:在所有u∈U,V∈(V-U)的边(u,v)∈E中找一条代价最小的边(u0,v0)并入TE,同时v0并入U,知道U=V为止,此时TE中必有n-1条边,则T={V,{TE}}为N的最小生成树。

        具体代码如下:

//最小生成树Prim算法;void MiniSpanTree_Prim(MGraph* G){int min,i,j,k;int closest[MaxVex];//保存最近顶点的下标;int lowcost[MaxVex];//保存相关顶点间的权值;for (i=0;i<MaxVex;i++){lowcost[i]=G->arc[0][i]; //将与V0顶点有边的权值存入数组;closest[i]=0;//数组closest初始化都为0;}for (i=1;i<MaxVex;i++){min=Infinity;//初始化最小权值为∞;j=0;k=0;while(j<MaxVex)//循环全部顶点;{if (lowcost[j]!=0&&lowcost[j]<min){//如果权值不为0且权值小于min;min=lowcost[j];   //则让当前权值成为最小值;k=j;//k是用来存储最小权值的顶点小标;}j++;}cout<<closest[k]<<k<<endl;//打印当前顶点边中权值最小边;lowcost[k]=0;   //将当前顶点权值设置为0,表示此顶点已经完成任务;//下面的循环目的是刷新lowcost数组和closest数组;for (j=0;j<MaxVex;j++){if ((lowcost[j]!=0)&&(G->arc[k][j]<lowcost[j])){//若下标为k顶点个边权值小于此前这些顶点未被加入生成树权值;lowcost[j]=G->arc[k][j];    //将小值存入lowcost;closest[j]=k;               //将下标为k的顶点存入closest;}}}}
         在Prim算法中lowcost数组记录从U中顶点到V-U中顶点候选边的权值,其目的是为了求出最小边。只需要将从顶点j到V-U中顶点的边的权值(最多n-1条边)与原来lowcost数组中对应边的权值进行比较,保存较小者即可。

        上面的代码,是默认从下标为0的顶点开始生成最小树的。该函数运行完后,lowcost数组应全部是0表示所有顶点都被访问到,closest数组存储的是下标顶点距离最近的顶点下标,利用closest就可以完成最小生成树的连线。

        2、克鲁斯卡迪尔(Kruscal)算法

        同样的思路,同样的思路我们也可以直接就以边为目标去构建,因为权值是在边上,直接去构造最小权值的边来构建生成树也是很自然的想法,只不过构建时需要考虑是否会形成环路而已,此时需要用到边集数组结构。

        假设N=(V,{E})是连通网,则令最小生成树的初始状态为只有n个顶点而无边的非连通图T={V,{}},图中每个顶点自成一个连通分量。在E中选择代价最小的边,若改边依附的顶点落在T中不同的连通分量上,则将此边加入到T中否则舍弃此边,而选择下一条代价最小的边。依次类推。直至T中所有顶点都在同一连通分量上为止。

void Kruscal_Tree(MGraph* G){int i,j,k;int u1,v1;int sn1,sn2;Edge edges[EdgeNum];  //定义边集数组,这里的边集数组已经按权值的大小拍好顺序;int vset[MaxVer];//定义一组数组来判断边与边是否形成回路;for (i=0;i<MaxVer;i++)vset[i]=i;  //初始化时每个顶点属于单独的连通分量,并进行编号;k=1;//k表示当前构造生成树的第几条边,初值为1;j=0;while(k<EdgeNum){//edges数组已经按权值大小排序,从权值最小的边开始;u1=edges[j].begin;v1=edges[j].end;//分别得到两个顶点所属的集合编号;sn1=vset[u1];sn2=vset[v1];if (sn1!=sn2){cout<<"边("<<u1<<","<<v1<<"),权为"<<edges[j].weight<<endl;k++;//生成树边数+1;for (i=0;i<MaxVer;i++)if (vset[i]==sn2)//集合编号为sn2的改为sn1;vset[i]=sn1;}j++; //扫描下一条边;}}

0 0