Bellman-Ford算法及其队列优化(SPFA)

1、算法概述算法

        Bellman-Ford算法解决的是通常状况下的单源最短路径问题。所谓单源最短路径问题:给定一个图G=(V,E),咱们但愿找到从给定源结点s属于V到每一个结点v属于V的最短路径。单源最短路径问题能够用来解决许多其余问题,其中包括下面几个最短路径的变体问题。包括单目的最短路径问题、单结点最短路径问题、全部结点对最短路径问题,这里不详细介绍。回到bellman-Ford,在这里,边的权重能够为负值。给定带权重的有向图G=(V,E)和权重函数W : E-->R,Bellman-Ford算法返回一个布尔值,以代表是否存在一个从源点能够到达的权重为负值的环路。若是存在这样的环路,算法将告诉咱们不存在解决方案。若是没有这种环路存在,算法将给出最短路径和它们的权重。边的权值能够为负数、实现简单,缺点是时间复杂度太高,高达O(VE)。但算法能够进行若干种优化,提升了效率。函数

     天然语言描述测试

对有向图G(V,E),用贝尔曼-福特算法求以V_s为源点的最短路径的过程:优化

  • 创建dist[],表示目前已知源点到各个节点的最短距离,起始值dist[s]=0,其他皆为\infty
  • 创建Pred[]Pred[]表示某节点路径上的父节点,起始值皆为NULL。
  • (V_i,V_j) \in E,比较dist[V_i]+(V_i,V_j)dist[V_j],并将小的赋给dist[V_j],若是修改了dist[V_j]pred[V_j]=V_i(松弛操做)
  • 重复以上操做V-1
  • 再重复操做一次,如dist[V_j]>dist[V_i]+(V_i,V_j),则此图存在负权环。

    伪代码表示
spa

BellmanFord(G,s)
 for i = 0 to n-1 do
  dist[i]= ∞
  Pred[i]= 0
 dist[s]=0
 for k = 1 to n-1 do
    foreach (V_i,V_j)E do
      if dist[V_i]+(V_i,V_j)<dist[V_j] do
          dist[V_j]=dist[V_i]+(V_i,V_j)
         Pred[V_j]=V_i
foreach (V_i,V_j)E do
      if dist[V_i]+(V_i,V_j)<dist[V_j] do
         return "No Shortest Path"
 return dist[]

2、原理htm

       为何说最短路径不存在负环呢?队列

       若是图G包含从s能够达到的权重为负值的环路,则最短路径权重无定义。从s到该环路上的任意结点的路径都不多是最短路径,由于咱们只要沿着任何“最短”路径再遍历一次权重为负值的环路,则老是能够找到一条权重更小的路径。若是从结点s到结点v的某条路径上存在权重为负值的环路,咱们定义s到v的最短路径等于负无穷。
ip

        松弛操做get

        它的原理是对图进行V-1次松弛操做,获得全部可能的最短路径。对于一条边(u,v)的松弛过程为:首先测试一下是否能够对源点s到v的最短路径进行改善。测试的方法是,将从结点s到结点u之间的最短路径距离加上结点u与v之间的权重,并与当前的s到v的最短路径估计进行比较,若是前者更小,则对v.d(源点s到v的最短路径) 和v.π(前驱结点)进行更新。
it

        每次松弛操做其实是对相邻节点的访问,第n次松弛操做保证了全部深度为n的路径最短。因为图的最短路径最长不会通过超过V-1条边,因此可知贝尔曼-福特算法所得为最短路径。

        负边权操做

        与迪科斯彻算法不一样的是,迪科斯彻算法的基本操做“拓展”是在深度上寻路,用于有向无环图的最短路径算法对每条边仅松弛一次。Bellman-Ford“松弛”操做则是在广度上寻路,这就肯定了贝尔曼-福特算法能够对负边进行操做而不会影响结果。

       负权环断定

        由于负权环能够无限制的下降总花费,因此若是发现第n次操做仍可下降花销,就必定存在负权环。

3、队列优化——SPFA

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

       实现方法:创建一个队列,初始时 里只有起始点,再创建一个表格记录起始点到全部点的最短路径(该表格的初始值要赋为极大值,该点到他自己的路径赋为0)。而后执行松弛操做,用队列里有的点去刷新起始点到全部点的最短路,若是刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空
      判断有无负环:若是某个点进入队列的次数超过N次则存在负环 (存在负环则无最短路径,若是有负环则会无限松弛,而一个带n个点的图至多松弛n-1次 。 参考:算法导论、http://zh.wikipedia.org/zh-cn/%E8%B4%9D%E5%B0%94%E6%9B%BC-%E7%A6%8F%E7%89%B9%E7%AE%97%E6%B3%95#.E6.9D.BE.E5.BC.9B
相关文章
相关标签/搜索