图的应用 - dijkstra算法(单源最短路径)

354 阅读3分钟

广度优先搜索求单源最短路径

对于一个无权图, 想要求出两个点间的最短路径, 可以用广度优先搜索实现, 但是对于有权图, 就需要使用dijkstra算法了.

dijkstra算法的手算过程

以下图为例, 我们想求出V1到V4的最短路径是什么

image.png

首先我们需要三个辅助数组来帮助我们完成最短路径的求解

  • dist[] 距离数组: 记录最短路径的距离;
  • path[] 路径数组: 记录当前结点在最短路径的前驱结点;
  • visited[] 标记数组: 标记已经找到的最短路径结点;
1. 初始化三个数组

v1是起点dist距离是0, v1没有前驱所以path是-1, 标记true;

image.png

2. 更新v1到其他结点的距离

v1邻接点是v2, v6, v7; 对应的距离分别是12, 16, 14;

12<∞; dist[1]更新∞为12; path[1]更新为0;

16<∞; dist[5]更新∞为16; path[5]更新为0;

14<∞; dist[6]更新∞为14; path[6]更新为0;

在未标记的结点中找到距离最小的结点min{12,∞,∞,∞,16,14}=12, 标记v2结点是找到的最短路径结点

image.png

3. 更新经过v2到其他结点的距离

v2邻接点是v3, v6; 对应的距离分别是10, 7;

12+10=22<∞; dist[2]更新为22; path[2]更新为1;

12+7=19>16; dist[5]不更新; path[5]不更新;

在未标记的结点中找到距离最小的结点min{22,∞,∞,16,14}=14, 标记v7结点是找到的最短路径结点

image.png

4. 更新经过v7到其他结点的距离

v7邻接点是v6, v5; 对应的距离分别是9, 8;

14+9=23>16; dist[5]不更新; path[5]不更新;

14+8=22<∞;  dist[4]更新为22; path[4]更新为6;

在未标记的结点中找到距离最小的结点min{22,∞,22,16}=16, 标记v6结点是找到的最短路径结点

image.png

5. 更新经过v6到其他结点的距离

v6邻接点是v3, v5; 对应的距离分别是6, 2;

16+6=22=22; dist[2]不更新; path[2]不更新;

16+2=18<22; dist[4]更新为18; path[4]更新为5;

在未标记的结点中找到距离最小的结点min{22,∞,18}=18, 标记v5结点是找到的最短路径结点

image.png

6. 更新经过v5到其他结点的距离

v5邻接点是v3, v4; 对应的距离分别是5, 4;

18+5=23>22; dist[2]不更新; path[2]不更新;

18+4=22<∞; dist[3]更新为22; path[3]更新为4;

在未标记的结点中找到距离最小的结点min{22,22}=22, 标记v3结点是找到的最短路径结点(也可标记v4)

image.png

7. 更新经过v3到其他结点的距离

v3邻接点是v4; 对应的距离是3;.

22+3=25>20; dist[3]不更新; path[3]不更新;

在未标记的结点中找到距离最小的结点min{20}=20, 标记v4结点是找到的最短路径结点

image.png

此时就求出v1到其他顶点的最短路径表. v1到v4的最短距离是22, 路径为:v1 -> v6 -> v5 -> v4

dijkstra算法的局限性

dijkstra算法只适用于权值为非负数的情况, 当权值为负值时就会失去作用. 比如下面这种情况, 采用dijkstra算法计算v0到v2的最短路径为7这样的错误结果, 实际应该10-5=5为最短

image.png

v0v1v2
dist[]0107
path[]-100
visited[]truetruetrue

dijkstra算法的证明

dijkstra算法是每次找到新的顶点经过set中的点, 到达起点的最短路径, 将其作为全局最短路径.

证明: 算法经过第k步, 找到的第k个顶点经过set中的点到起点的最短路径 = 全局最短路径.

当k=1时, 第1个顶点(起点)经过set中的点到起点的最短路径是0, 全局最短路径是0.

那么存在k, 执行到第k步时, 证明成立

设k+1步时选择了顶点v, 该顶点经过set中的点与起点相连路径长度最小

设u是set中的点, 起点经过u点与v点相连.

记: 起点经过u到v的距离是dist[v] (代表起点经过set中的u点到set外的v点距离最小),

起点到v的全局最短路径是short[v]

image.png

证明dist[v] = short[v]

那么我们假设, dist[v] ≠ short[v], 那么至少存在一点y1, y2, ..., yn不属于set与v相连是全局最短路径

image.png

short[v] = dist[y1] + 距离[y1-y2] + 距离[y2-y3] + ... + 距离[yn-v]

image.png

此时

dist[v] <= dist[y1] (当且仅当y1与v点重合时等号成立)

dist[v] <= short[v] (当且仅当y1,y2,...,yn与v点重合时等号成立)

与 假设至少存在一点y1, y2, ..., yn不属于set与v相连是全局最短路径 不符

所以dist[v] = short[v]

即起点经过set中的点到set外一点的最短路径 就是 全局最短路径

参考:

[1] 数据结构系列24-最短路径dijkstra算法 | tyrantlucifer

[2] 6.5单源最短路径问题BFS和Dijkstra算法的概念和正确性证明 考研《数据结构C语言版》严蔚敏知识点讲解_哔哩哔哩_bilibili

[3] 图的基础算法(五) -- Dijkstra 精讲_哔哩哔哩_bilibili

[4] 详解Dijkstra算法(含数学证明和优化)_dijkstra算法的空间复杂度_数据刘的博客-CSDN博客