最短路径
最短路径 (shortest paths) 的相关实际场景比较广泛,比如地图、网络等。
单源最短路径 (SSSP / single-source shortest paths) 是求解给定某一源点到其所有可达点的最短路径,即使得这些无权路径的边数或者带权路径的权重和最小。
Dijkstra (/ˈdaɪkstrə/) 算法解决的是非负权图的 SSSP,未使用堆查找优化时,也被称为 Dijkstra 暴力算法。Dijkstra 译作“迪杰斯特拉“。
松弛 (Relax)
"松弛"这个术语出现得较多,含义同数学意义上的松弛相同,减少约束。图的两点之间存在多条路径,找到最短的一条需要比较,每比较一次就减少一次约束。
但我认为此处从数学中沿用这个命名并不好。
上图表示在 SSSP 中,忽略原图中的其他点和边,探索过程中某一时刻点 A 对其邻接点的松弛。
红框中的下标:
- 第一个:在当前探索范围内,源点到该点的的距离。
- 第二个:相应路径上的父节点。和各顶点一样,都是实际以数字存储,-1 表示没有父节点。
观察 A B 两点状态,3 + 1 < 5,说明 A 点所处路径向 B 延伸后比此前源点到 B 的路径更短,松弛有效。
同理可得对 C 松弛有效,对 D 松弛无效。
如果之后某刻 A 点再次被有效松弛了,那么应该继续松弛 B C D 点。
术语补充
读者可能不熟悉图论基础知识的一些符号,特此解释一部分。
- :图
- : 点集
- :边集
- : 权重函数
- : 任意
- : 从点 到点 的边
- : 属于
所以 表示边集中任意边的权重 。 其他的相关符号还有:
- 或者 表示集合 的 size
- 表示图中 两点的距离(最短路径的权重和)。
- 表示向点集 中加入点 。
- 表示邻接表
原理
如上图所示,给定图 。 为源点,求其到各可达点的最短路径。
设红框区域中的点集为 ,表示 中前 个从 出发最近的点。
记某时刻 。这与 Prim 算法很相似。
从 外的可直达点 中选择离 最近的 点,记录相应路径 和其长度。
从 外的不可直达点 中任选一点记为 ,路径 上一定经过 或 ,记该点为 ,路径为 。
既然 ,则
根据 点的选择条件可知 ,结合上式可得
所以 为 即 中离 最近的点,此前记录的 为最短路径。
如上所示,令 。再执行上一步,逐渐扩张即可找到 到所有可达点的最短路径。
初始
Dijkstra 算法和 Prim 算法相比,取得周围最近点的思路有所变化,详见下面的算法实现。
求解
表示长度为 ,元素都为 的数组。 读者在初次接触伪代码时会不习惯。但据笔者的调查反馈,习惯后体验还是不错的。具体的术语和符号均在《图论入门》的附录中有介绍。
松弛部分被我抽离为一个函数,此函数表示松弛点 的所有邻接点。
完整代码
思考为什么松弛前不用判断点 的邻接点是否 settled?
答:因为处于 settled 的点已经找到最短距离,松弛会一直无效。
除 Settled 外还有一些常见的命名,如 Visited、Done、Used。从语义上来看,Settled 和 Done 更加合适一些。
在很多只考虑距离的应试题中, 的更新可以简写为 。
复杂度
时间: 空间:
与 Prim 算法的复杂度分析大体相同,但本章考虑不可达导致提前截止的情况,所以时间复杂度不是 。还可以用堆查找优化,但实现稍复杂,这在《图论进阶》中有讲到。
练习
给你一个由 n 个节点(下标从 0 开始)组成的无向加权图,该图由一个描述边的列表组成,其中 edges[i] = [a, b]
表示连接节点 a 和 b 的一条无向边,且该边遍历成功的概率为 succProb[i] 。
指定两个节点分别作为起点 start 和终点 end ,请你找出从起点到终点成功概率最大的路径,并返回其成功概率。
如果不存在从 start 到 end 的路径,请 返回 0 。只要答案与标准答案的误差不超过 1e-5 ,就会被视作正确答案。
示例1:
输入:n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.2], start = 0, end = 2
输出:0.25000
解释:从起点到终点有两条路径,其中一条的成功概率为 0.2 ,而另一条为 0.5 * 0.5 = 0.25
求解:
Dijkstra 算法中的权重累计方式是相加,秉承非递减原则,最短路径上的权重和最小。
本题的权重累计方式是相乘,但概率 ,秉承非递增原则,求解概率最大的路径。
所以可以镜像转换松弛过程,代码如下所示。
根据原题中的提示部分(未展示在这里),可知该图是稀疏图,所以主流解法是二叉堆查找优化的版本。但这不属于内地本科的教学范围,你正常写 Dijkstra 暴力算法一般来说也是能过的。二叉堆查找优化正好也属于《图论进阶》中免费的试读部分,读者有兴趣可以结合《图论入门》中 Prim 算法的两种堆查找优化看一下。
《图论入门》 和 《图论进阶》 是我在 Leetcode 上写的教程,包括更清晰的 PPT 展示、供读者思考的折叠区域、以及配套的练习题。
读者如有困惑尽管指出。也欢迎提出写法建议,不甚感谢!