Dijkstra 算法是一种求单源最短路的算法,即从一个点开始到所有其他点的最短路。其基本原理是:每
次新扩展一个距离最短的未被扩展的点,更新与其相邻的点的距离。当所有边权都为正时,由于不会存在
一个距离更短的没扩展过的点,所以这个点的距离永远不会再被改变,因而保证了算法的正确性。不过根
据这个原理,用 Dijkstra 求最短路的图不能有负权边,因为扩展到负权边的时候会产生更短的距离,有可
能就破坏了已经更新的点距离不会改变的性质。Dijkstra 算法每一次操作分两步:
(1)找到所有未被更新过的节点中,与源点距离最短的点 k。
(2)用该节点更新所有点与源节点的距离:
在维护所有点与源点的距离时,可用堆来维护。另外,如果要求最短路径,在更新距离时,加入前序
节点即可。
Dijkstra 算法复杂度为 O(N 2 )。
Bellman-Ford 算法也是一种单源最短路的算法,可以解决边权为负的问题。其基本原理为:每次调整
枚举所有的边,可以通过这条边更新距离值,则更新,直到没有点的值调整了为止(或者调整 n-1 次)。
其更新函数与 Dijkstra 的更新函数一样。不同的是,Bellman-Ford 需要枚举边,其复杂度为 O(NM),高
于 Dijkstra,但是 Bellman-Ford 可以处理负权和负环问题。对于负环,如果调整 n-1 次以后,还可以继续调
整,则表示图中有负环。
SPFA 算法是对 Bellman-Ford 算法的一种加强。由于 Bellman-Ford 算法中枚举边的操作非常冗余,事实
上,只有边对应的顶点有更新,才需要枚举这条边。SPFA 就用队列维护顶点,来达到减少枚举的目的。
SPFA 执行时,维护一个队列。当某个节点距离值被更新时,加入队列。对于队首元素,枚举以它为起
始顶点的边,更新对应节点的距离。对于负环问题,SPFA 执行时,需要判断顶点更新次数,当顶点更新次
数大于等于 n 时,则有负环。
Floyd 算法与前两种算法不同,它是求解顶点之间两两最短距离。其基本思想是,如果节点 i.k 的距离
加上 k,j 的距离小于 i,j 的距离,则更新 i,j 的距离。该思想与动态规划的思想类似。需要注意的是,在枚举
的过程中,需要先枚举 k,再枚举 ij。Floyed 算法时间复杂度为 O(N 3 )。虽然该复杂度与枚举起始节点的
Dijkstra 算法一致,但是 Floyed 算法常数复杂度比 Dijkstra 小很多。