[迭代优化]Bellman-Ford最短路径算法

【原理】

这个算法也是求最短路径时常用的算法,但由于其复杂度为O(EV)其中E、V分别为边的总数和顶点的总数。

如果当为稠密图时,E也是O(V^2)的级别,因此整体复杂度会达到O(E^3)。如果是稀疏图,则无法达到使用优先队列的Dijkstra算法的O(E*logV)。因此,Bellman-Ford算法更适合于稠密图。

但是它相比Dijkstra算法更好的一点就是,它能够处理带有负权值的边。但是依然无法处理负权值的回路,如果遇到的话会检测到,从而退出程序。

算法思想如下:

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]中。

大致的证明如下:

首先指出,图的任意一条最短路径既不能包含负权回路,也不会包含正权回路,因此它最多包含|v|-1条边。

其次,从源点s可达的所有顶点如果 存在最短路径,则这些最短路径构成一个以s为根的最短路径树。Bellman-Ford算法的迭代松弛操作,实际上就是按顶点距离s的层次,逐层生成这棵最短路径树的过程。

在对每条边进行1遍松弛的时候,生成了从s出发,层次至多为1的那些树枝。也就是说,找到了与s至多有1条边相联的那些顶点的最短路径;对每条边进行第2遍松弛的时候,生成了第2层次的树枝,就是说找到了经过2条边相连的那些顶点的最短路径……。因为最短路径最多只包含|v|-1 条边,所以,只需要循环|v|-1 次。

每实施一次松弛操作,最短路径树上就会有一层顶点达到其最短距离,此后这层顶点的最短距离值就会一直保持不变,不再受后续松弛操作的影响。(但是,每次还要判断松弛,这里浪费了大量的时间,怎么优化?单纯的优化是否可行?)

如果没有负权回路,由于最短路径树的高度最多只能是|v|-1,所以最多经过|v|-1遍松弛操作后,所有从s可达的顶点必将求出最短距离。如果 d[v]仍保持 +∞,则表明从s到v不可达。

如果有负权回路,那么第 |v|-1 遍松弛操作仍然会成功,这时,负权回路上的顶点不会收敛。

【代码】

算法伪代码如下:

Bellman-Ford(G,w,s) :boolean   //图G ,边集 函数 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

算法的具体实现如下(给出了主要函数和定义):

#define N

typedef
struct
{
    
int
src, dst, len;
} edge[N];

//the current shortest distance

int dis[N];

//
v is the number of vertices
//e is the number of edges

bool bellman_ford(int src)
{
    
for (int i = 0; i < v; ++
i)
         dis[i]
=
INT_MAX;
     dis[src]
= 0
;
    
    
//relax

    for (int i = 1; i < v; ++ i)
        
for (int j = 0; j < e; ++
j)
            
if (dis[edge[j].src] != INT_MAX &&

                 dis[edge[j].dst]
> dis[edge[j].src] +
edge[j].len)
                 dis[edge[j].dst]
= dis[edge[j].src] +
edge[j].len;
    
    
//if there is negative cycle

    for (int i = 0; i < e; ++ i)
        
if (dis[edge[i].src] != INT_MAX &&

             dis[edge[i].dst]
> dis[edge[i].src] +
edge[i].len)
            
return false
;

    
return true
;
}

另外的算法优化的方法(包括SPFA)可以见下面的链接:

http://hi.baidu.com/jzlikewei/blog/item/94db7950f96f995a1038c2cd.html

http://hi.baidu.com/jzlikewei/blog/item/5343d134b54c6f48251f14cd.html

你可能感兴趣的:(最短路径)