Bellman-Ford的队列优化

来源:互联网 发布:如寡人者 安与知耻 编辑:程序博客网 时间:2024/05/21 17:20

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的最大值大1int 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
原创粉丝点击