第四章 图(八) - 《算法》读书笔记
第四章 图(八)
4.4.5 无环加权有向图中的最短路径算法
按照拓扑排序顺序放松顶点,就能在和E+V成正比的时间内解决无环加权有向图的单点最短路径问题。
- 算法特点:
- 能够在线性时间内解决单点最短路径问题
- 能够处理负权重的边
- 能够解决相关的问题,例如找出最长的路径
4.4.6 一般加权有向图中的最短路径问题
4.4.6.2 尝试II
- Dijkstra算法根据距离起点的远近一次检查路径,但这是基于添加一条边会使路径变得更长的假设
4.4.6.3 负权重的环
定义:加权有向图中的负权重环是一个总权重(环上所有边的权重之和)为负的有向环。
当且仅当加权有向图中至少存在一条从s到v的有向路径且所有从s到v的有向路径上的任意顶点都不存在于任何负权重环中时,s到v的最短路径才是存在的。
4.4.6.4 尝试III
- 一个定义明确且可以解决加权有向图最短路径问题的算法要能够:
- 对于从起点不可达的顶点,最短路径为正无穷
- 对于从起点可达但路径上的某个顶点属于一个负权重环的顶点,最短路径为负无穷
- 对于其他所有顶点,计算最短路径的权重(以及最短路径树)
Bellman-Ford算法:在任意含有V个顶点的加权有向图中给定起点s,从s无法到达任何负权重环,以下算法能够解决其中的单点最短路径问题:将distTo[s]初始化为0,其他distTo[]元素初始化为无穷大。以任意顺序放松有向图的所有边,重复V轮。
Bellman-Ford算法所需的时间和EV成正比,空间和V成正比
4.4.6.5 基于队列的Bellman-Ford算法
- 使用FIFO队列记录上一轮distTo[]发生变化的顶点,只有从这些顶点指出的边才能够改变其他distTo[]元素的值
4.4.6.6 实现
- 具体实现如下:
public class BellmanFordSP{
private double[] distTo;
private DirectedEdge[] edgeTo;
private boolean[] onQ;//该顶点是否存在于队列中
private Queue<Integer> queue;
private int cost;//relax()的调用次数
private Iterable<DirectedEdge> cycle;
public BellmanFordSP(EdgeWeightedDigraph G, int s){
distTo = new double[G.V()];
edgeTo = new DirectedEdge[G.V()];
onQ = new boolean[G.V()];
queue = new Queue<Integer>();
for(int v = 0; v < G.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY;
distTi[s] = 0.0;
queue.enqueue(s);
onQ[s] = true;
while(!queue.isEmpty() && !hasNegativeCycle()){
int v = queue.dequeue();
onQ[v] = false;
relax(G, v);
}
}
private void relax(EdgeWeightedDigraph G, int v){
for(DirectedEdge e : G.adj(v)){
int w = e.to();
if(distTo[w] > distTo[v] + e.weight()){
distTo[w] = distTo[v] + e.weight();
edgeTo[w] = e;
if(!onQ[w]){
queue.enqueue(w);
onQ[w] = true;
}
}
if(cost++ % G.V() == 0)
findNegativeCycle();
}
}
}
//负权重环相关方法见后面章节
private void findNegativeCycle();
public boolean hasNegativeCycle();
public Iterable<Edge> negativeCycle();
对于任意含有V个顶点的加权有向图和给定的起点s,在最坏情况下,基于队列的Bellman-Ford算法解决最短路径问题(或者找到从s可达的负权重环)所需的时间与EV成正比,空间和V成正比。
4.4.6.8 负权重环的检测
- 查找负权重环的实现如下所示:
private void findNegativeCycle(){
int V = edgeTo.length;
EdgeWeightedDigraph spt;
spt = new EdgeWeightedDigraph(V);
for(int v = 0; v < V; v++)
if(edgeTo[v] != null)
spt.addEdge(edgeTo[v]);
EdgeWeightedCycleFinder cf;
cf = new EdgeWeightedCycleFinder(spt);
cycle = cf.cycle();
}
- 当所有边放松V轮之后,当前仅当队列非空时有向图中才存在从起点可达的负权重环
- 如果是这样,edgeTo[]数组所表示的子图中必然含有这个负权重环

浙公网安备 33010602011771号