一、Bellman-Ford算法
用于求最短路径,适用边的权值为负数或正数的(Dijkstra只适用于正权值)
1、算法方法:
n个顶点,m条边的图:
最多有n-1个循环,每个循环里对每一条边都执行松弛操作,此时就会有些顶点已经求得最短路,即这些顶点的最短路的“估计值”变成“确定值”。此后这些顶点的最短路的值就会一直保持不变,不再受后续松弛操作的影响。
一句话概括:对所有的边进行n-1次“松弛”操作,主要代码为4行:
for(int k=1;k<=n-1;k++)
for(int i=1;i<=m;i++)
if(dis[v[i]]>dis[u[i]]+w[i])
dis[v[i]]=dis[u[i]]+w[i];
比如1点到2点的权值为3,那么判断dis[2]>dis[1]+3,如果大于,就把dis[2]置成dis[1]+3的值,就这样把m条边全部遍历一遍。
时间复杂度为O(NM),其实可以优化,就是在每次松弛完成后,检查dis数组是否有更新,没有更新就提前结束循环。
2、代码:
int i;
for(int k=1;k<=n-1;k++)
{
for(i=1;i<=n;i++)
bak[i]=dis[i];//添加一个一位数组来备份数组dis
for(i=1;i<=m;i++)
if(dis[v[i]]>dis[u[i]]+w[i])
dis[v[i]]=dis[u[i]]+w[i];
for(i=1;i<=n;i++)
if(bak[i]!=dis[i]){
check=1;
break
}
if(check==0)
break;
}
二、队列优化的Bellman-Ford算法
1、原理:
只有那些在前一遍松弛中改变了最短路径估值的顶点,才能引起它们邻接点最短路径估计值发生变化。因此用一个对列来存放被成功松弛的顶点,之后只对队列中的点进行处理,这就降低了算法的时间复杂度。
2、算法方法:
每次仅对最短路估值发生变化了的顶点的所有边执行松弛操做;如果知道哪些点的最短路径发生了变化呢?可以用一个队列来维护。
struct
{
int head;
int tail;
int que[m];
}
初始时将源点加入队列,每次从队首(head)取出一个顶点,并对与其相邻的所有顶点进行松弛尝试,若某个相邻的顶点松弛成功,且这个相邻的顶点不再队列中(不在head和tail之间),则将它加入队列中。对当前顶点处理完毕后立即出队,并对下一个新队首进行如上操作,直到队列为空时算法结束。
最坏的时间复杂度也是O(NM)。
国人段凡丁的《关于最短路径的SPFA快速算法》就是一种基于队列优化的Bellman-Ford算法。