使用SPFA算法检测负循环

时间:2013-08-02 02:52:36

标签: algorithm path cycle shortest

在具有负和正权重的有向图中使用下面的SPFA算法,我们如何检测负循环?

程序最短路径更快算法(G,s)

  1    for each vertex v ≠ s in V(G)
  2        d(v) := ∞
  3    d(s) := 0
  4    push s into Q
  5    while Q is not empty
  6        u := pop Q
  7        for each edge (u, v) in E(G)
  8            if d(u) + w(u, v) < d(v) then
  9                d(v) := d(u) + w(u, v)
 10                if v is not in Q then
 11                    push v into Q

2 个答案:

答案 0 :(得分:1)

我的回答内容主要来自this article

在有关Bellman-Ford算法的原始论文中,已经证明,在没有负周期的有向加权图中,最短路径的长度最多为| V | -1个边。

我们可以利用这一事实来检测SPFA的负周期。我们只需要保留一个额外的数组即可存储每个顶点当前最短路径的长度(以边数为单位)。一旦这个数字达到| V |对于任何顶点,我们都知道图中有一个负周期。

下面是用于使用SPFA检测负周期的伪代码。已对其进行了修改,以使每个顶点的起始“最短路径”为0。这等效于创建虚源 s 并将 s 连接到每个具有边的顶点的权重为0。即使没有连接图形,这也可以确保找到负循环。

function SPFA(G):
    for v in V(G):
        len[v] = 0
        dis[v] = 0
        Queue.push(v)
    while !Queue.is_empty():
        u = Queue.pop()
        for (u, v) in E(G):
            if dis[u] + w(u, v) < dis[v]:
                len[v] = len[u] + 1
                if len[v] == n:
                    return "negative cycle detected"
                dis[v] = dis[i] + w(u, v)
                if !Queue.contains(v):
                    Queue.push(v)
    return "no negative cycle detected"

如果您想找到消极周期,可以阅读上面链接的文章,因为它也可以解决这一问题。

答案 1 :(得分:0)

SPFA每次看到“更好”的边缘时都会将新节点放入队列,以最大限度地缩短总距离,这只是Bellman-Ford的更好修剪。 Bellman-Ford的算法证明了非负加权循环的最大值为 | V | - 1个边缘。

因此,要检查周期是否为负加权,您只需要在运行SPFA期间检查是否至少 | V | 边缘。换句话说,检查您是否至少访问过同一节点 | V | 次。

这是一个附加的伪代码:

procedure SPFA(G, s)
    for each vertex v ≠ s in V(G)
        d(v) := ∞
        visits(v) := 0
    d(s) := 0
    push s into Q
    while Q is not empty
        u := pop Q
        visits(u) := visits(u) + 1                      // increment visit count
        if visits(u) < |V| then                         // relaxation step
            for each edge (u, v) in E(G)
                if d(u) + w(u, v) < d(v) then
                    d(v) := d(u) + w(u, v)
                    if v is not in Q then
                        push v into Q

但是,请注意,这不会使所有节点成为负权重循环的一部分。要获取属于负权重周期的所有节点,请执行DFS或BFS以标记从 u 可到达的所有节点,其中访问( u )= | V |

这是最终修改的伪代码:

procedure DFS(G, u, visited)
    if visited(u) = false then
        visited(u) := true
        for each edge (u, v) in E(G)
            DFS(G, v, visited)

procedure SPFA(G, s)
    for each vertex v ≠ s in V(G)
        d(v) := ∞
        visits(v) := 0
    d(s) := 0
    push s into Q
    while Q is not empty
        u := pop Q
        visits(u) := visits(u) + 1                      // increment visit count
        if visits(u) < |V| then                         // relaxation step
            for each edge (u, v) in E(G)
                if d(u) + w(u, v) < d(v) then
                    d(v) := d(u) + w(u, v)
                    if v is not in Q then
                        push v into Q
    for each vertex u in V(G)
        visited(u) := false
    for each vertex u in V(G), where visits(u) = |V|
        DFS(G, u, visited)
    for each vertex u in V(G), where visited(u) = true
        d(u) := -∞