广度优先搜索求单源最短路径
对于一个无权图, 想要求出两个点间的最短路径, 可以用广度优先搜索实现, 但是对于有权图, 就需要使用dijkstra算法了.
dijkstra算法的手算过程
以下图为例, 我们想求出V1到V4的最短路径是什么
首先我们需要三个辅助数组来帮助我们完成最短路径的求解
- dist[] 距离数组: 记录最短路径的距离;
- path[] 路径数组: 记录当前结点在最短路径的前驱结点;
- visited[] 标记数组: 标记已经找到的最短路径结点;
1. 初始化三个数组
v1是起点dist距离是0, v1没有前驱所以path是-1, 标记true;
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结点是找到的最短路径结点
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结点是找到的最短路径结点
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结点是找到的最短路径结点
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结点是找到的最短路径结点
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)
7. 更新经过v3到其他结点的距离
v3邻接点是v4; 对应的距离是3;.
22+3=25>20; dist[3]不更新; path[3]不更新;
在未标记的结点中找到距离最小的结点min{20}=20, 标记v4结点是找到的最短路径结点
此时就求出v1到其他顶点的最短路径表. v1到v4的最短距离是22, 路径为:v1 -> v6 -> v5 -> v4
dijkstra算法的局限性
dijkstra算法只适用于权值为非负数的情况, 当权值为负值时就会失去作用. 比如下面这种情况, 采用dijkstra算法计算v0到v2的最短路径为7这样的错误结果, 实际应该10-5=5为最短
| v0 | v1 | v2 | |
|---|---|---|---|
| dist[] | 0 | 10 | 7 |
| path[] | -1 | 0 | 0 |
| visited[] | true | true | true |
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]
证明dist[v] = short[v]
那么我们假设, dist[v] ≠ short[v], 那么至少存在一点y1, y2, ..., yn不属于set与v相连是全局最短路径
short[v] = dist[y1] + 距离[y1-y2] + 距离[y2-y3] + ... + 距离[yn-v]
此时
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