**最短路径快速算法**（英語：），国际上一般认为是带有队列优化的Bellman-Ford 算法，一般仅在中国大陆被称为**SPFA**，是一个用于求解有向带权图单源最短路径的算法。这一算法在随机的稀疏图上表现出色，并且适用于带有负边权的图。 然而SPFA在最坏情况的时间复杂度与 Bellman-Ford 算法相同，因此在非负边权的图中使用堆优化的Dijkstra 算法效率可能优于SPFA。 SPFA算法首先在1959年由Edward F. Moore（英语：）作为广度优先搜索的扩展发表，相同算法在1994年由段凡丁重新发现。

算法
--

给定一个有向带权图{\displaystyle G=(V,E)}和一个源点s，SPFA算法可以计算从s到图中每个节点{\displaystyle v}的最短路径。其基本思路与 Bellman-Ford 算法相同，即每个节点都被用作用于松弛其相邻节点的备选节点。但相较于 Bellman-Ford 算法，SPFA算法的先进之处在于它并不盲目地尝试所有节点，而是维护一个备选的节点队列，并且仅有节点被松弛后才会将其放入队列中。整个流程不断重复，直至没有节点可以被松弛。

下面是这个算法的伪代码。这里的{\displaystyle Q}是一个备选节点的先进先出队列，{\displaystyle w(u,v)} 是边{\displaystyle (u,v)}的权值。

一个基于欧氏几何距离的SPFA算法。红线是当前状态下的各条最短路径。蓝线表示松弛发生的地方，也即通过在{\displaystyle Q}中用节点 u 连接{\displaystyle v}可以给出一条从源点到{\displaystyle 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*
```

对于无向图，将每条无向边视作两条有向边以采用 SPFA 算法。

最坏情况下的性能
--------

下面是一种触发该算法低性能表现的数据构造方式。假设要求从节点1到节点n的最短路径。对于整数{\displaystyle 1\leq i<n}，考虑添加边{\displaystyle (i,i+1)}并令其权为一个随机的小数字（于是最短路应为1-2-...-n），同时随机添加{\displaystyle 4n}条其他的权较大的边。在这种情况下，SPFA算法的性能表现将会非常低下。

SPFA算法本质上依然被认为是Bellman-Ford算法的一个特例，因此一般认为SPFA算法的最差复杂度是{\displaystyle O(|V|\cdot |E|)}，其中|V|为点数，|E|为边数。

NOI2018中，出题人使用特殊构造图卡到SPFA算法的最坏情况，并在讲题时在幻灯片上打出关于"SPFA它死了"的字样，导致现今很多中国大陆OI题目都会构造特殊数据卡掉SPFA，于是关于SPFA已死的说法广泛流传于中国大陆。[原創研究？]

优化技巧
----

SPFA算法的性能很大程度上取决于用于松弛其他节点的备选节点的顺序。事实上，如果{\displaystyle Q}是一个优先队列，则这个算法将很类似于Dijkstra 算法。然而尽管这一算法中并没有用到优先队列，仍有多种可用的技巧可以用来提升队列的质量，借此能够提高平均性能（但仍无法提高最坏情况下的性能）。其中，最著名的两种技巧通过重新调整{\displaystyle Q}中元素的顺序从而使得更靠近源点的节点能够被更早地处理。因此一旦实现了这两种技巧，{\displaystyle Q}将不再是一个先进先出队列，而更像一个链表或双端队列。

**距离小者优先** （**Small Label First**(**SLF**)）（由Bertsekas在Networks, 第23期, 1993, P703-P709中最先提出)。在伪代码的第十一行，将总是把{\displaystyle v}压入队列尾端修改为比较{\displaystyle d(v)} 和 {\displaystyle d{\big (}{\text{front}}(Q){\big )}}，并且在{\displaystyle d(v)}较小时将{\displaystyle v}压入队列的头端。这一技巧的伪代码如下（这部分代码插入在上面的伪代码的第十一行后）：

```
 **procedure** Small-Label-First(*G*, *Q*)
     **if** d(back(*Q*)) < d(front(*Q*)) **then**
         u := pop back of *Q*
         push u into front of *Q*
```

**距离大者置后** （**Large Label Last**(**LLL**)）（由Bertsekas、Guerriero、与Musmanno在JOTA, 第88期, 1996, 页297-320最先提出)。在伪代码的第十一行，我们更新队列以确保队列头端的节点的距离总小于平均，并且任何距离大于平均的节点都将被移到队列尾端。伪代码如下：

```
 **procedure** Large-Label-Last(*G*, *Q*)
     *x* := average of d(*v*) for all *v* in *Q*
     **while** d(front(*Q*)) > *x*
         *u* := pop front of *Q*
         push *u* to back of *Q*
```

参考文献
----

1. 1 2 3 .  [2018-05-25]. （原始内容存档于2020-11-17）.
2. ↑  SPFA算法 （页面存档备份，存于）
3. ↑  Moore, Edward F.. Harvard University Press: 285–292. 1959.  `|contribution=`被忽略 (帮助) SPFA is Moore's "Algorithm D".
4. ↑  Duan, Fanding, 西南交通大学学报 [Journal of Southwest Jiaotong University], 1994, **29** (2): 207–212  [2018-05-25], （原始内容存档于2019-04-25）
5. ↑ .  [2018-05-25]. （原始内容存档于2021-01-16）.

扩展阅读
----

* 夏正冬; 卜天明; 张居阳.. 《计算机科学》. 2014, **41** (6): 180–184  [2020-11-17]. （原始内容存档于2020-12-08）.