带有负权边的最短路径问题

2018-03-13 17:08:57

最短路径问题是图论中一个经典的问题,Dijkstra算法更是大名鼎鼎。然而纵是如此著名的算法也有其不擅长的领域,也就是带有负权边的图是无法使用Dijkstra算法来进行最短路计算的。理由也很简单,每次dijkstra都是将目前的额最短路添加到集合中,这也就保证了,下一次的最短路径是肯定要长于之前添加进去的最短路径的,然而在有负权边的时候,这个推论就会被打破,最后会导致整个Dijkstra算法崩溃。

那么,难道带有负权边的最短路径问题就没有解法了么?

答案显然是否定的,这里就会介绍两种求解带有负权边的图的最短路径求解的算法,一是Bellman Ford算法,二是SPFA算法

一、Bellman Ford算法

贝尔曼-福特算法(英语:Bellman–Ford algorithm),求解单源最短路径问题的一种算法,由理查德·贝尔曼(Richard Bellman) 和 莱斯特·福特 创立的。有时候这种算法也被称为 Moore-Bellman-Ford 算法,因为Edward F. Moore 也为这个算法的发展做出了贡献。它的原理是对图进行V-1次松弛操作,得到所有可能的最短路径。其优于迪科斯彻算法的方面是边的权值可以为负数、实现简单,缺点是时间复杂度过高,高达O(VE)。但算法可以进行若干种优化,提高了效率。

Bellman Ford算法每次对所有的边进行松弛,每次松弛都会得到一条最短路径,所以总共需要要做的松弛操作是V - 1次。在完成这么多次松弛后如果还是可以松弛的话,那么就意味着,其中包含负环。

procedure BellmanFord(list vertices, list edges, vertex source)
   // 该实现读入边和节点的列表,并向两个数组(distance和predecessor)中写入最短路径信息

   // 步骤1:初始化图
   for each vertex v in vertices:
       if v is source then distance[v] := 0
       else distance[v] := infinity
       predecessor[v] := null

   // 步骤2:重复对每一条边进行松弛操作
   for i from 1 to size(vertices)-1:
       for each edge (u, v) with weight w in edges:
           if distance[u] + w < distance[v]:
               distance[v] := distance[u] + w
               predecessor[v] := u

   // 步骤3:检查负权环
   for each edge (u, v) with weight w in edges:
       if distance[u] + w < distance[v]:
           error "图包含了负权环"

二、SPFA算法

求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm。 SPFA算法是西南交通大学段凡丁于1994年发表的。松弛操作必定只会发生在最短路径前导节点松弛成功过的节点上,用一个队列记录松弛过的节点,可以避免了冗余计算。复杂度可以降低到O(kE),k是个比较小的系数(并且在绝大多数的图中,k<=2,然而在一些精心构造的图中可能会上升到很高)。

SPFA算法可以认为是对Bellman Ford算法的优化,但不论如何,这是在最短路径问题中由中国学生提出的算法,算法的核心思路并不复杂,主要的操作就是维护一个candidate队列,最开始的时候只有s,然后对s的邻接边进行松弛,如果可以松弛,则判断一下当前结点是否在队列中,如果不在的话,那么将该结点加入队列,重复操作,知道队列为空。

从理性的角度来分析这个算法的合理性:

只要最短路径存在,上述SPFA算法必定能求出最小值。证明:每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点v的最短路径估计值d[v]变小。所以算法的执行会使d越来越小。由于我们假定图中不存在负权回路,所以每个结点都有最短路径值。因此,算法不会无限执行下去,随着d值的逐渐变小,直到到达最短路径值时,算法结束,这时的最短路径估计值就是对应结点的最短路径值。

SPFA判环:

当一个结点入队次数到达V的时候,表明其中有环。如同Bellman Ford算法,只需要V - 1次就可以完成所有的松弛,如果做了 V 次松弛,那么说明有环。

具体实现中可以用一个数组保存每个结点的入队次数。

 procedure Shortest-Path-Faster-Algorithm(G, s)
  1    for each vertex v ≠ s in V(G)
  2        d(v) := ∞
  3    d(s) := 0
  4    offer s into Q
  5    while Q is not empty
  6        u := poll Q
  7        for each edge (u, v) in E(G)
  8            if d(u) + w(u, v) < d(v) then
  9                d(v) := d(u) + w(u, v)
 10                if v is not in Q then
 11                    offer v into Q

原文地址:https://www.cnblogs.com/hyserendipity/p/8559241.html