Dijkstra算法
Dijkstra算法是基础图论中非常重要的算法。算法能够算出某一个点到图中其他任何点的最短路劲及其权重,要求图中所有的边的权重都是非负值。
算法中需要用到优先队列,优先队列的操作看这里
算法维护两个顶点集合S,Q,以及一个估计最短路径数组d,d[i]值表示从源点到顶点i的估计最短路径。任意时刻d[i]都代表了从源点source到顶点i,在只经过S中的顶点的情况下的最短路径,所以当S扩充为整个图时,d就保存了所有的最短路径。
如果需要分别列出源点到任意顶点的最短路径,只需要为每个顶点加一个前驱,表示从源点通过最短路径到i点时,该路径上的前一个节点。即假如A -> B -> C为一条最短路径,则C->π = B。
算法通过松弛操作保证d[i]代表了只经过S的情况下的最短路径。即
RELAX(u, v, w)
if (d[v] > d[u] + w(u,v)) {
d[v] = d[u] + w(u,v)
v.π = u
}
松弛操作满足下面性质:
以v.d表示顶点v的估计最短路径的权重, D(s, v)表示s到v的最短路径的权重。那么:
(1) 假设s~u->v是图中的一条最短路径,只要在执行RELAX(u, v, w)的任意时刻,u.d = D(s, u),即u的估计最短路径就是实际的最短路径,那么执行RELAX(u, v, w)之后,v.d = D(s, v),即松弛操作之后,v的估计最短路径也就是实际最短路劲。
(2)假设v1, v2, v3, v4 ... vi是图中以v1为源节点的一条最短路径,那么只要我们的松弛操作序列存在如下一条子序列,RELAX(v1, v2, w), RELAX(v2, v3, w) 。。。RELAX(vi-1, vi, w),那么在RELAX(vi-1, vi, w)操作完成时,v1.d, v2.d 。。vi.d都代表了最短路径的权值,并且这与其他任何松弛操作无关。
(3)u.d >= D(s,u),并且在进行数次松弛操作达到S(s,u)后不再变化。
初始时S为空,Q包含了图的所有顶点,并且按照没个顶点的估计最短路径值即d[i]值为关键字,维护成一个优先级队列(小根堆)。
Dijkstra
S = NULL
Q = G
while (!Q.empty) {
u = HEAP-EXTRACT-MIN(Q)
S.insert(u)
for(从u出发的每一条边v)
RELAX(u, v, w)
}
回顾上面松弛操作的性质,可以用反证法证明在每个节点u被加入到S时,u.d = D(source, u),即此时源点source到u的估计距离就是最短路径。证明如下:
1) 最开始s到s自己肯定是最短路径,s.d=0(因为所有路径的权值都是非负的)
2) 然后松弛所有从s出发的边。很显然其中至少有一条是最短的。(假设s出发的节点为u_0, u_1...u_n, 假设每个节点都没有最短路径经过,那么对任意一个节点都存在与s直连的xi节点使得s -> xi ~ u_i 比 s -> u_i短,并且这个xi 不属于{u_0, u_1...}这与s跟x直连相矛盾)
3) 所以选出来从s出发的最短的那条就是那个实际的最短路径,也就是伪代码中的s -> u,依次类推,由于s -> u 就是实际的最短路径,那么从u出发的那条最小权重的路径,也就是实际的路径,最后S集合包含整个图时,就得到了到每个节点的最短路径
下面一个Dijkstra算法的简单实现,没有求前驱节点,没有维护优先级队列。每次取最小值的时候直接遍历了一遍,人懒没药治- -!
#include <iostream> #include <deque> #include <vector> #include <queue> #include <climits> #include <set> using namespace std; //从图中在S集合意外的节点中选一个最短路径权值最小的,相当于extract-min(Q) int minOne(vector<vector<int>> &g, int t, set<int> &s){ int ans = 0; int min = INT_MAX; for(int i = 0; i < g.size(); ++i){ if (g[t][i] < min && s.find(i) == s.end()){ min = g[t][i]; ans = i; } } cout << ans; return ans; } vector<int> dijkstra(vector<vector<int>> &g, int source){ vector<int> sp(g.size(), 0); set<int> s; s.insert(source); for(int i = 0; i < g.size(); ++i){ sp[i] = g[source][i]; } int t = source; for(int i = 1; i < g.size(); ++i){ int u = minOne(g, t, s); s.insert(u); t = u; for(int i = 0; i < g.size(); ++i){ sp[i] = sp[i] > sp[u] + g[u][i] ? sp[u] + g[u][i] : sp[i]; } } return sp; }
浙公网安备 33010602011771号