克鲁斯卡尔算法&普里姆算法

克鲁斯卡尔算法  
假设  WN=(V,{E})  是一个含有  n  个顶点的连通网,则按照克鲁斯卡尔算法构造最小生成树的过程为:先构造一个只含  n  个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树上的根结点,则它是一个含有  n  棵树的一个森林。之后,从网的边集  E  中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直至森林中只有一棵树,也即子图中含有  n-1条边为止。

普里姆算法  
假设  WN=(V,{E})  是一个含有  n  个顶点的连通网,TV  是  WN  上最小生成树中顶点的集合,TE  是最小生成树中边的集合。显然,在算法执行结束时,TV=V,而  TE  是  E  的一个子集。在算法开始执行时,TE  为空集,TV  中只有一个顶点,因此,按普里姆算法构造最小生成树的过程为:在所有“其一个顶点已经落在生成树上,而另一个顶点尚未落在生成树上”的边中取一条权值为最小的边,逐条加在生成树上,直至生成树中含有  n-1条边为止。  
--------------------------------------------------------------  
克鲁斯卡尔算法(1)

void kruskal (edgeset ge, int n, int e)
// ge为权按从小到大排序的边集数组
{        
	int set[MAXE], v1, v2, i, j;
    for (i=1;i<=n;i++)
		set[i]=0;	// 给set中每个元素赋初值
	i=1;// i表示获取的生成树中的边数,初值为1
	j=1;// j表示ge中的下标,初始值为1
    while (j<n && i<=e)
	// 检查该边是否加入到生成树中
    {
		v1=seeks(set,ge[i].bv);
		v2=seeks(set,ge[i].tv);
		if (v1!=v2)		// 当v1,v2不在同一集合,该边加入生成树
		{
			printf("(%d,%d)",ge[i].bv,ge[i].tv);
			set[v1]=v2;
			j++;
		}
		i++;
    }
}
克鲁斯卡尔算法(2)

//kruskal  
void  MiniSpanTree_Kruskal(CGraph  gn)  
{  
//按算法构造网gn的最小生成树并输出生成树上各条边/  
   T.init(gn.vexes,null);    
   hp.init(gn)    //堆初始化,堆中元素        
   heap_sort(hp);    //为网gn的所有边          
   while(  (hp.Last>0)  &&  (T.Arcn<g.vexn-1)  )      
   {  
       e  =  hp.fo[1];  //取堆中最小代价边                          
       fix_mfset(T.set,e.v1,i);    //确定边的两端点      
       fix_mfset(T.set,e.v2,j);    //所在的集合            
       if(i  !=  j)  
       {                                                        
           T.addArc(e.v1,e.v2);    //增加一条边              
           mix_mfset(T.set,i,j);  //合并两个集合                      
       }        
       hp.fo[1]=hp.fo[Last];    
       dec(hp.Last);          
       if(  hp.Last>1  )              
           heap_sift(hp,1,hp.Last);//求下一条最小代价边      
   }  
   if(  T.Arcn<g.vexn-1  )                                        
       error("Not  Connected")/网gn非连通                
}//endp  Kruskal  


------------------------------------------------------------------------------  

 普里姆算法

//prim  

#include  <stdio.h>  
#include  <stdlib.h>  
 
#define  MAXVEX  30  
#define  MAXCOST  1000  
 
/*每一步扫描数组lowcost,在V-U中找出离U最近的顶点,令其为k,并打印边(k,closest[k])*/  
/*然后修改lowcost和closest,标记k已经假如U  。c表示图邻接矩阵,弱不存在边(i,j),则c[i][j]的值为一个大于任何权而小于无限打的阐述,这里用MAXCOST表示*/  
void  prim  (int  c[MAXVEX][MAXVEX],  int  n)/*一直图的顶点为{1,2,...,n},c[i][j]为(i,j)的权,打印最小生成树的每条边*/  
{  
           int  i,j,k,min,lowcost[MAXVEX],closest[MAXVEX];  
           for  (i=2;i<=n;i++)  /*从顶点1开始*/  
           {  
                       lowcost[i]=c[1][i];  
                       closest[i]=1;  
           }  
           closest[1]=0;  
           for  (i=2;i<=n;i++)  /*从U之外求离U中某一顶点最近的顶点*/  
           {  
                       min=MAXCOST;  
                       j=1;  
                       k=i;  
                       while  (j<=n)  
                       {  
                                   if  (lowcost[j]<min  &&  closest[j]!=0)  
                                   {  
                                               min=lowcost[j];  
                                               k=j;  
                                   }  
                                   j++;  
                       }  
                       printf  ("(%d,%d)",closest[k],k);  /*打印边*/  
                       closest[k]=0;  /*  k假如到U中  */  
                       for  (j=2;j<=n;j++)  
                                   if  (closest[j]!=0  &&  c[k][j]<lowcost[j])  
                                   {  
                                               lowcost[j]=c[k][j];  
                                               closest[j]=k;  
                                   }  
           }  
}  
 
void  main()  
{  
           int  n=7,i,j,mx[MAXVEX][MAXVEX];  
           for  (i=0;i<=n;i++)  
                       for  (j=0;j<=n;j++)  
                                   mx[i][j]=MAXCOST;  
           mx[1][2]=50;  
           mx[1][3]=60;  
           mx[2][4]=65;  
           mx[2][5]=40;  
           mx[3][4]=52;  
           mx[3][7]=45;  
           mx[4][5]=50;  
           mx[5][6]=70;  
           mx[4][6]=30;  
           mx[4][7]=42;  
           printf("最小生成树边集:\n");  
           prim(mx,n);  
}  

/****************************************************************************************/

我们这里讨论的是无向图的最小生成树,有向图的最小生成树算法比较复杂,如果感兴趣可以参看清华大学的《信息学奥林匹克竞赛指导-图论的算法与程序设计》。

所谓最小生成树,就是给定一个无向图,挑选若干条边,连成一个树行图(无圈),使得所选边的权至和最小。

一下算法中,n是点数,e是边数。

1.Prime

初始时任选一点s为树根,每次选出一条权最小的边[i,j],使得点i在树中,点j不在树中,将j和[i,j]加入树中,重复n-1次求出最小生成树。

我们用二叉堆来提高效率。二叉堆中存储边,初始时二叉堆中存储与s关联的边,每次取出一个权最小的边[i,j],删除[i,j],同时将与j关联的边放入二叉堆中,注意判重。

因为所有边只放入和取出二叉堆一次,所以算法复杂度是(eloge)。

2.Kruskal

从所有边中找到一个最小的边,且将改变放入后不会生成圈,重复n-1次后求出最小生成树。

我们首先将所有边排序,然后从小到大判断,如果不产生圈就加入树中,当加入n-1条边时停止。

为了判断是否组成圈,我们要用到并查集,相关知识可以在本站或任一本竞赛书中找到,这里不赘述。

算法复杂度是(eloge+eα),α是做一次并查集的复杂度,可以认为是常数。

两种算法同样优秀。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值