Bellman-Ford的队列优化

Bellman-Ford的队列优化

思想:每次仅对最短路程发生变化了的点的相邻边执行松弛操作。
如何知道当前哪些点的最短路程发生变化?此处可以用一个队列来维护这些点。

每次选取队首顶点u,对顶点u的所有出边进行松弛操作。例如有一条边u→v的边,如果通过u→v这条边使得源点到顶点v的最短路径变短(dis[u]+e[u][v]<dis[v]),且顶点v不在当前队列中,就将顶点v放入队尾。需要注意:同一个顶点同时在队列中出现多次是毫无意义的,所以我们需要一个数组来判重(判断哪些点已经在队列中)。在对顶点u的所有出边松弛完毕后,就将顶点u出队。接下来不断从队列中取出新的队首顶点再进行如上操作,直至队列空为止。

5 7

1 2 2
1 5 10
2 3 3
2 5 7
3 4 4
4 5 5
5 3 6

第一行2个整数n、m,n为顶点个数,m为边的个数。接下来m行,每行3个数x、y、z,表示顶点x到顶点y的边权值为z。 

#include <stdio.h>
int main() {
	int n,m,i,j,k;
	int u[8],v[8],w[8];			//u、v、w数组大小要根据实际情况来设置,要比m的最大值大1
	int first[6],next[6];		//first next数组大小要根据实际情况来设置,要比n的最大值大1 
	int dis[6]={0},book[6]={0};	//book数组用来记录哪些顶点已经在队列中 
	int que[101]={0},head=1,tail=1;	//定义一个队列,并初始化队列 
	int inf=99999999;
	
	scanf("%d %d",&n,&m);
	//初始化dis数组,这里是1号顶点到其余各个顶点的初始路程 
	for(i=1;i<=n;i++)
		dis[i]=inf;
	dis[1]=0;
	
	for(i=1;i<=n;i++)	//初始化book数组,初始化为0,刚开始都不在队列中 
		book[i]=0; 
	
	for(i=1;i<=n;i++)		//初始化first数组下标1~n的值为-1,表示1~n顶点暂时都没有边
 		first[i]=-1; 
 		
 	for(i=1;i<=m;i++) {
		scanf("%d %d %d",&u[i],&v[i],&w[i]);//读入每一条边 
		next[i]=first[u[i]];	//关键语句 ,first[u[i]]保存顶点u[i]的第一条边的编号,next[i]存储“编号为i的边”的“下一条边”的编号 
		first[u[i]]=i;
	}	
	
	//1号顶点入队 
	que[tail]=1;tail++;
	book[1]=1;			//标记1号顶点已经入队 
	while(head<tail) {		//队列不为空的时候循环 
	 	k=first[que[head]]; //当前需要处理的队首顶点 
	 	while(k!=-1) {		//扫描当前顶点所有的边 
	 		if(dis[v[k]] > dis[u[k]+wki]]) { 	//判断是否松弛成功 
 				dis[v[k]] = dis[u[k]+w[k]];		//更新顶点1到顶点v[k]的路程 
 				if(book[v[k]]==0) {			//book数组用来判断顶点v[k]是否在队列中。如果不使用一个数组来标记的话,判									//断一个顶点是否在队列中,每次需要从队列的head到tail扫一遍,很浪费时间
					que[tail]=v[k];	//入队		 
 					tail++;
 					book[v[k]]=1;	//同时标记顶点v[k]已经入队 
				 }
		 	}
		 	k=next[k];
	 	} 
 	
 		book[que[head]]=0;		//出队 
 		head++;
	}
 	
	 for(i=1;i<=n;i++)		//输出1号顶点到其余各个顶点的最短路径 
	 printf("%d ",dis[i]);
	 
	getchar();getchar();
	return 0;
 	
} 

总结:初始时将源点加入队列。每次从队首head取出一个顶点,并对与其相邻的所有顶点进行松弛尝试,若某个相邻的顶点松弛成功,且这个相邻的顶点不在队列中(不在head~tail之间),则将它加入到队列中。对当前顶点处理完毕后立即出队,并对下一个新队首进行如上操作,直到队列为空时算法结束。此处用了一个book来记录每个顶点是否处在队列中。也可以不要book数组,检查一个顶点是否在队列中,只需要把que[head]到que[tail]依次判断一遍就可以,但这样做的时间复杂度是O(N),而使用book数组来记录的话时间复杂度会降至O(1).

使用队列优化的Bellman-Ford算法的时间复杂度在最坏情况下也是O(NM)。通过队列优化的Bellman-Ford算法如何判断一个图是否有环?如果某个点进入队列的次数超过n次,那么这个图肯定存在负环。

用队列优化的Bellman-Ford算法的关键之处在于:只有那些在前一遍松弛中改变了最短路程估计值的顶点,才可能引起它们邻接点最短路程估计值发生改变。因此,用一个队列来存放被成功松弛的顶点,之后只对队列中的点进行处理,这就降低了算法的时间复杂度。


最短路径算法分析
 FloydDijkstraBellman-Foed队列优化的Bellman-Ford
空间复杂度O(N*N)O(M)O(M)O(M)
时间复杂度O(N*N*N)O((M+N)logN)O(MN)最坏O(MN)
适用情况 稠密图(和顶点关系密切) 稠密图(和顶点关系密切) 稀疏图(与边关系密切) 稀疏图(与边关系密切)
负权 可以解决不能解决可以解决可以解决




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值