6.4 最短路径:Bellman-Ford优化
Bellman-Ford优化
我们知道,在最开始的算法中,我们每一次操作后就会进行一次松弛的判断
实际上,这浪费了我们的时间:每次操作后有些顶点的最短路就不会变化了
实际上我们可以这样做:每次仅仅对最短路的估计值发生了变化的顶点的所有出边执行松弛操作
实操
我们可以利用队列来维护这些点
我们每次都选取队首的顶点u,对顶点u的所有出边进行松弛操作
如果通过u->v这条边,可以使得源点到顶点v的最短路径变短,且顶点v不在当前队列中
那么我们就把顶点v放入队尾
在对顶点u的所有出边松弛完成之后,就将u出队
实际上,我们就是在完成“得到队首,更新判断队首,出队进行下一轮”的操作
优化代码
#include <stdio.h>
int main()
{
int n,m,i,j,k ;
int u[8],v[8],w[8];
//uvw的值要根据实际情况来,要比m的最大值大1
int first[6],next[8] ;
//first的值要比n的最大值大1,next要比m的最大值大1
int dis[6]={0},book[6]={0} ; //book数组用来记录哪些顶点在队列中,可以降低复杂度
int que[101] = {0} , head = 1 , tail = 1 ; //定义队列并初始化
int inf = 99999999 ; //用inf存储我们认为的正无穷值
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[i] = 0 ;
for(i=1;i<=n;i++)
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]] = i ;
}
//从1号顶点开始,1号顶点入队
que[tail] = 1 ;
tail++;
book[1] = 1 ;
while(head<tail)
{
k=first[que[head]] ;
while(k!=-1){
//扫描附近所有边
if(dis[v[k]] > dis[u[k]] + w[k]){
dis[v[k]] = dis[u[k]] + w[k] ; //更新1到v[k]的路程
//下面利用book来判断某点是否在队列中
if(book[v[k]] == 0){
//0就是不在队列中
que[tail] = v[k] ;
tail++;
book[v[k]] = 1 ;//同时标记顶点v[k]已经入队
}
}
k = next[k];
}
//出队
book[que[head]] = 0 ;
head++;
}
//输出1号顶点到其余各个顶点的最短路径
for(i=1;i<=n;i++)
printf("%d",dis[i]);
getchar();getchar();
return 0 ;
}
其关键之处在于:只有那些在前一遍松弛中改变了最短路程估计值的顶点,才可能引起它们邻接点最短路径估计值发生改变
另外,这种优化在形式上和宽度优先搜索类似,不过宽度优先中出队的元素不会返回队列,而这里要看情况而定