最短路
注意,下面的所有算法的图的存储方式都是使用了vector的方式,如果对存储方式有问题,请看我的另一篇博客:图的存储。
Bellman-Ford
- 基于 松弛(relax) 操作。
设起始节点为 S S S。
定义 d i s t ( u ) dist(u) dist(u)为 S S S到当前节点 u u u的最短路径长度。
松弛操作 r e l a x ( u , v ) relax(u,v) relax(u,v)是指: d i s t ( v ) = m i n { d i s t ( v ) , d i s t ( u ) + e d g e _ l e n ( u , v ) } dist(v) = min\{dist(v), dist(u) + edge\_len(u,v)\} dist(v)=min{ dist(v),dist(u)+edge_len(u,v)}
因此有三角不等式: d i s t ( v ) ≤ d i s t ( u ) + e d g e _ l e n ( u , v ) dist(v) \le dist(u) + edge\_len(u,v) dist(v)≤dist(u)+edge_len(u,v)
-
支持负权。
-
能找到某个节点出发到所有节点的最短路,或者报告某些最短路不存在。
-
SPFA是Bellman-Ford的一种优化。
-
算法核心:对所有的边进行松弛操作,直到不能进行松弛操作为止.
-
终止条件:由于一次 松弛 会让最短路的边数至少+1,而最短路边数最多为 V − 1 V-1 V−1 , V V V 是图中节点的个数。因此,至多执行 V − 1 V-1 V−1 次松弛操作。
代码实现:
void bellman_ford(){
vector<int> dis(vertex_size, INF);
int S = 2;
dis[S] = 0;
cout << "(bellman_ford)\nS = " << S << ":\t";
for (auto elem : T[S]){
dis[elem.first] = elem.second;
}
for (int i = 0; i < vertex_size - 1; i ++){ // 进行V-1次松弛操作,因为最短路至多有V-1条边
bool flag = false; // 如果某一次没有进行松弛操作了,那么可以结束。
for (int u = 0; u < vertex_size; u ++){
for (auto elem : T[u]){
if (dis[elem.first] > dis[u] + elem.second){
dis[elem.first] = dis[u] + elem.second;
flag = true;
}
}
}
if (!flag){
//cout << "OVER" << endl;
break;
}
}
for (auto i : dis){
cout << i << " ";
}
cout << endl;
}
- 复杂度分析:
- 时间复杂度: O ( V ⋅ E ) O(V{\cdot}E) O(V⋅E),V是顶点数,E是边数。
- 空间复杂度: O ( 1 ) O(1) O(1),除了必要的存储空间(比如,最短路数组dis和图的存储T).
例题:判断一个图中是是否有负环。
解答:先对所有边进行V-1次松弛操作(执行bellman_ford),然后,对所有边进行一次松弛操作,如果在这次松弛中,松弛成功了,那么一定存在负环。否者,不存在。
SPFA
- 是bellman-ford的优化算法。
- 在bellman-ford算法中,有时候我们不需要那么多的无用 松弛 。
- 只有上一次被松弛的节点v,v所连接的边才可能引起下一次的松弛操作。
- 用队列来维护“哪些结点可能会引