最短路
1. 知识结构图:
说明:
- 约定:n为点数,m为边数。
- 上面当所有边权为正数时,有两种选择:1. 朴素Dijkstra算法;2. 堆优化版的Dijkstra算法。当稠密图时(m和n^2相当),尽量使用前者;当稀疏图时,使用后者堆排序版的Dijkstra算。
- SPFA一般情况下,比Bellman-Ford要快。但如果求的是“对边数做一个限制,经过的边数<=k的话,就无法使用SPFA算法,只能使用Bellman-Ford算法。”
2. 侧重点
以后遇到的最短路问题,不会让你证明算法的正确性。难点是让你建图,如何将原问题抽象成一幅图,如何定义点和边,使得问题变成最短路问题,然后套用模板来做。即难点在建图,不在算法的原理上。
其它补充知识:Dijkstra算法,基于贪心;Floyd算法,基于动态规划;Bellman-Ford算法,基于离散数学的知识。
3. 朴素Dijkstra算法
算法流程:
- 初始化距离。1号点到起点的距离初始化为0,其它点距离都初始化为正无穷。dist[1] = 0, dist[i] = INF。
- 迭代n次。
for v : 1 ~ n
2.1 寻找不在s中的距起点距离最近的点t。【s:当前已确定最短距离的点】
2.2 将t放到s中。s = t。
2.3 用t更新其它所有点的距离。比如:针对某点j,使用 从起点到t加上从t到j的距离 更新 从起点到j的距离。
算法模板:
【AcWing——最短路-朴素Dijkstra】AcWing 849. Dijkstra求最短路 I
4. 堆优化版的Dijkstra算法
首先:堆优化版的Dijsktra算法适合稀疏图。
堆优化版的Dijkstra算法,对朴素版的Dijkstra算法中步骤2.1、2.3,使用堆(优先队列)的数据结构对其进行了实现。另外,因为是稀疏图,存储图采用邻接表的方式实现。
具体来看的话:
- 朴素版的时间复杂度主要来自于步骤2.1,寻找距离最近的点t时遍历全部节点n,时间复杂度是O(n)。而使用堆时,这个操作是O(1)的。
- 朴素版步骤2.3更新所有点的距离时,复杂度为O(n),而使用堆来实现时,这个操作为O(mlogn)【堆操作此时是遍历邻近的所有边O(m),然后堆上进行push复杂度O(logn)】。
算法模板:
【AcWing——最短路-堆优化版的Dijkstra算法】AcWing 850. Dijkstra求最短路 II
5. Bellman-Ford算法
算法流程:
说明:
从算法流程中可以看出,Bellman-Ford算法的时间复杂度为O(n*m)。
就两重循环,第一重循环n次,第二重循环所有边(上图中a, b, w代表:a到b的边权重为w),每次循环的时候更新一下最短距离就可以了。更新之后,该算法证明了所有边的距离一定满足dist[b] <= dist[a] + w
,这个不等式称为三角不等式。遍历所有边更新的过程称为松弛操作。
要注意一点,Bellman-Ford算法是处理有负权边的最短路问题。而求最短路时,如果有负权回路时,最短路是不一定存在的。 比如下面:从1开始走,走到2时,没走一遍2->3->5->2这个回路,权值都会减1,所以这样走下去最短路变为了负无穷。
所以如果图里面有负权回路的话,Bellman-Ford算法求出来的结果将会变为负无穷。(也就是说,能求出最短路的话,题里面肯定是没有负权回路的)。
但其实Bellman-Ford算法是可以求出来图中是否有负权回路的。上面算法流程中第一层循环迭代的此时实际上是有实际意义的。比如迭代k次之后,我们求的最短距离(dist数组)代表从1号点经过不超过k条边走到每个点的最短距离。 所以如果迭代的时候,第n次迭代的时候又更新了某些边的话,那就说明存在一条最短路,它上面的边数是大于等于n的, 也就有n+1个点,而1~n只有n个点,又抽屉原理知,一定有两个点编号一样,那这条路径上就一定存在环。路径上存在环,它是更新过后出现的,所以这个环一定是负环。
所以如果第n次迭代的时候,有边更新的话,那说明存在边数为n的最短路径,就说明存在负环。但一般找负环不是用Bellman-Ford算法来做(时间复杂度比较高),而是用SPFA来做。
算法模板:
【AcWing——Bellman_Ford算法】AcWing 853. 有边数限制的最短路
6. SPFA
SPFA是对bellman-ford算法的优化(使用宽度优先搜索)。
算法流程
算法模版
【最短路——SPFA】AcWing 851. spfa求最短路
此外,还可以使用SPFA判断负环。使用下方的cnt变量辅助。
判断负环例题:【最短路——Bellman_Ford算法】AcWing 853. 有边数限制的最短路
7. Floyd算法
Floyd可用于求多源最短路。它可以处理负权,但不能有负权回路。
算法流程:
Floyd算法基于动态规划,代码很简单,就三重循环。
算法模版:
【最短路——Floyd算法】AcWing 854. Floyd求最短路