Bellman-Ford算法
Bellman-Ford算法的优点是可以发现负圈,缺点是时间复杂度比Dijkstra算法高。而SPFA算法是使用队列优化的Bellman-Ford版本,其在时间复杂度和编程难度上都比其他算法有优势。
Bellman-Ford算法流程分为三个阶段:
-
第一步:数组Distant[i]记录从源点s到顶点i的路径长度,初始化数组Distant[n]为INF, Distant[s]为0;
-
第二步:以下操作循环执行至多n-1次,n为顶点数: 对于每一条边e(u, v),如果Distant[u] + w(u, v) < Distant[v],则另Distant[v] = Distant[u]+w(u, v)。w(u, v)为边e(u,v)的权值; 若上述操作没有对Distant进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环;
-
第三步:为了检测图中是否存在负环路,即权值之和小于0的环路。对于每一条边e(u, v),如果存在Distant[u] + w(u, v) < Distant[v]的边,则图中存在负环路,即是说改图无法求出单源最短路径。否则数组Distant[n]中记录的就是源点s到各顶点的最短路径长度。
v的最短距离保存在 d[v]中。
在这个算法中,需要了解的几点
1、只有上一次迭代中松驰过的点才有可能参与下一次迭代的松弛操作。
似乎算法在遍历每一条边的做法会浪费时间,或许我们只需要考虑那些被成功松弛的点的邻点就可以了。我们可以用一个队列来维护这些被成功松弛的点,这个小小的改进可以节省很多时间,改进后的算法叫做SPFA。
2、迭代的实际意义:每一次迭代k中,我们找到了经历了k条边的最短路
3、没有点能够松弛时,迭代结束
Bellman-Ford(G,w,s) :boolean //图G ,边集 函数 w ,s为源点
for each vertex v ∈ V(G) do //初始化 1阶段
d[v] ←+∞
d[s] ←0; //1阶段结束
for i=1 to |v|-1 do //2阶段开始,双重循环。
for each edge(u,v) ∈E(G) do //边集数组要用到,穷举每条边。
If d[v]> d[u]+ w(u,v) then //松弛判断
d[v]=d[u]+w(u,v) //松弛操作 2阶段结束
for each edge(u,v) ∈E(G) do
If d[v]> d[u]+ w(u,v) then
Exit false
Exit true
SPFA
算法特点:在 Bellman-ford 算法的基础上加上一个队列优化,减少了冗余的松弛操作,是一种高效的最短路算法。
关键词:初始化,relax,队列
实现的过程和Bellman_Ford类似
【初始化】
dis数组全部赋值为INF,pre数组全部赋值为-1(表示还不知道前驱),
dis[s] = 0 表示源点不要求最短路径(或者最短路径就是0)。
【队列+松弛操作】
读取队头顶点u,并将队头顶点u出队(记得消除标记);将与点u相连的所有点v进行松弛操作,如果能更新估计值(即令d[v]变小),那么就更新,另外,如果点