[算法导论读书笔记]Bellman-Ford算法(单源最短路径)

来源:互联网 发布:国际编程证书 编辑:程序博客网 时间:2024/06/11 23:48

    Bellman-Ford算法与另一个非常著名的Dijkstra算法一样,用于求解单源点最短路径问题。Bellman-ford算法除了可求解边权均非负的问题外,还可以解决存在负权边的问题(意义是什么,好好思考),而Dijkstra算法只能处理边权非负的问题,因此 Bellman-Ford算法的适用面要广泛一些。但是,原始的Bellman-Ford算法时间复杂度为 O(VE),比Dijkstra算法的时间复杂度高,所以常常被众多的大学算法教科书所忽略,就连经典的《算法导论》也只介绍了基本的Bellman-Ford算法,在国内常见的基本信息学奥赛教材中也均未提及,因此该算法的知名度与被掌握度都不如Dijkstra算法。事实上,有多种形式的Bellman-Ford算法的优化实现。这些优化实现在时间效率上得到相当提升,例如近一两年被热捧的SPFA(Shortest-Path Faster Algoithm 更快的最短路径算法)算法的时间效率甚至由于Dijkstra算法,因此成为信息学奥赛选手经常讨论的话题。然而,限于资料匮乏,有关Bellman-Ford算法的诸多问题常常困扰奥赛选手。如:该算法值得掌握么?怎样用编程语言具体实现?有哪些优化?与SPFA算法有关系么?本文试图对Bellman-Ford算法做一个比较全面的介绍。给出几种实现程序,从理论和实测两方面分析他们的时间复杂度,供大家在备战省选和后续的noi时参考。

Bellman-Ford算法思想

    Bellman-Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题。对于给定的带权(有向或无向)图 G=(V,E),其源点为s,加权函数 w是 边集 E 的映射。对图G运行Bellman-Ford算法的结果是一个布尔值,表明图中是否存在着一个从源点s可达的负权回路。若不存在这样的回路,算法将给出从源点s到 图G的任意顶点v的最短路径d[v]。

Bellman-Ford算法流程分为三个阶段:

(1)    初始化:将除源点外的所有顶点的最短距离估计值 d[v] ←+∞, d[s] ←0;

(2)    迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)

(3)    检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 d[v]中。

算法描述如下:

Bellman-Ford(G,w,s) boolean   //,边集 函数 w s为源点

1        for each vertex v ∈ V(G) do        //初始化 1阶段

2            d[v] ←+∞

3        d[s] ←0;                             //1阶段结束

4        for i=1 to |v|-1 do               //2阶段开始,双重循环。

5           for each edge(u,v) ∈E(G) do //边集数组要用到,穷举每条边。

6              If d[v]> d[u]+ w(u,v) then      //松弛判断

7                 d[v]=d[u]+w(u,v)               //松弛操作   2阶段结束

8        for each edge(u,v) ∈E(G) do

9            If d[v]> d[u]+ w(u,v) then

10            Exit false

11    Exit true


代码示例:

#include <iostream>#include <stdio.h>using namespace std;#define MAX 100000#define N 1010int nNode, nEdge, original;typedef struct Edge{int u, v;int cost;}Edge;Edge edge[N];int d[N], pre[N];void Relax(int u, int v, int w){if(d[v] > d[u] + w ){d[v] = d[u] + w;pre[v] = u;}}void Initialize_Single_source(){for( int i = 1; i <= nNode; i++){d[i] = MAX;pre[i] = 0;}d[original] = 0;}bool Bellman_Ford(){Initialize_Single_source();for( int i = 1; i < nNode; i++ ){for( int j = 1; j <= nEdge; j++){Relax(edge[j].u, edge[j].v, edge[j].cost);}}for( int j = 1; j <= nEdge; j++){if( d[edge[j].u] > d[edge[j].v] + edge[j].cost){return false;}return true;}}void Print_Path(int root){  if(0 != root){Print_Path(pre[root]);printf("-->%d", root);}}int main(int argc, char* argv[]){scanf("%d%d%d", &nNode, &nEdge, &original);for( int i = 1; i <= nEdge; i++ ){scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].cost);}if (Bellman_Ford()){for( int i = 1; i <= nNode; i++){printf("\nnode%d: %d(km)\tPath:",i, d[i]);Print_Path(i);}printf("\n");}else{printf("Have negative circle\n");}return 0;}
测试结果:


参考资料:

http://www.cppblog.com/infinity/archive/2011/10/20/66621.html

http://blog.csdn.net/niushuai666/article/details/6791765