1 简介
Dijkstra(迪杰斯特拉)算法是非常经典的最短路径寻找算法,适用于加权图中寻找起点到终点的最短路径。
图是一种数据结构,包含节点和边。节点与节点之间通过边相连,加权图是指边上有权重,算法目标是寻找从起点到终点的一条路径,使得其中所有边的权重之和最小。
举例来说,我们要从北京开车导航到深圳,每一个城市都是节点,城市与城市之间的道路是边,道路的长短是边的权重。目标是规划一条路径,使得从起点到终点行驶的道路最短,查看经过了哪些城市。
2 原理
Dijkstra的核心思路就是:最短路径的子路径也最短。
假设最短路径的子路径不是最短,那么可以将此子路径替换为更短的子路径,替换后新的路径比最短路径还短。
通过分而治之找到所有最短子路径,最后连起来得到整体的最短路径。
算法需要维护几个数据:(1)每一步起点距离各节点当时最短的距离,以下称距离矩阵;(2)起点距离各节点当前最短距离对应的父节点,以下称父节点数据;(3)未完成集合和已完成集合。
算法流程图如下:
下面结合示例说明此过程
3 实例
求上图中A点到F点的最短路径。
第一步,A节点到自身是最短的,距离为0。将A放在已完成集合,并将A的父节点数据设置为A,表示从A节点走到当前节点的距离最短。查找A的邻居,分别为B和D,距离分别为3和4,其他点不相邻,距离为无穷大。
第二步,寻找当前距离矩阵最短的节点,为B点。此时A点到B点的最短距离就是3,因为假设有其他路径到达B点,比如A→X→B,A→X距离已经比A→B长了,再加上X→B的距离就更长了。需要做如下操作:
(1)将B移动到已完成集合,并标明B节点的父节点是A节点,表示经由A节点到B节点距离是最短的;
(2)查看B节点的邻居,包括C、D、E节点,更新距离矩阵。对于C来说,经由A→B→C的距离为3+8=11,前一步距离是∞,11<∞,因此更新距离为11,并设置C的父节点为B。对于D来说,经由A→B→C的距离为3+2=5,前一步距离是4,4<5,因此不更新距离。对于E来说同理,更新距离为8,设置父节点为B。
第三步,寻找当前距离矩阵最短的节点,为D点。此时4就是从A点到D点最短的距离。将D点移动到已完成节点集合。查看D点的邻居节点,已完成的B点不再考虑,因此只考虑E点。前一步距离为8,经由D点的距离为4+6=10,10>8,因此不更新距离。
第四步,寻找未完成节点中距离A最短的节点,为E。将E移动到已完成节点集合。查看E点的邻居节点,为C和F。经由E到C的最短距离是10,上一步距离是11,10<11,所以更新距离,并将C的父节点设置为E。经由E到F最短距离是13,上一步是∞,因此更新A到F的距离,并将F的父节点设置为E。
第五步,寻找未完成节点中距离A最短的节点,为C。将C移动到已完成节点集合。查看C点的邻居,为F。经由C到F的最短距离是10+7=17,大于前一步最短距离,因此不更新。
第六步,寻找未完成节点中距离A最短的节点,为F。将F移动到已完成节点集合。此时全部节点均完成,退出算法。
查找F的父节点为E,E的父节点为B,B的父节点为A,因此最短路径为A→B→E。这里每个子路径都是最短的,所以整体路径是最短的。
4 缺陷
此算法中的权重必须是非负的,因为每一步取距离矩阵中最短距离时,隐含了所有权重不能为负数的假设。如果有负数,假设取了距离矩阵最短的节点X,距离为10。有可能存在一个距离更长的节点Y,距离为12,但是后续Y点到X点的权重是-5,加起来为7,距离比10更短了,这样从起点到X点的最短距离就不是10了。