【问题标题】:How can I find the number of distinct shortest paths in undirected graph in linear time?如何在线性时间内找到无向图中不同最短路径的数量?
【发布时间】:2020-10-01 20:23:31
【问题描述】:

给定一条无向路径,在输入起始节点 u 和结束节点 v 后,不同最短路径数的算法是什么?该算法可以依赖于 |V|和 |E|,但必须是线性的。

证明它实际上返回任何 u 和 v 的不同最短路径的数量也会很有帮助。

【问题讨论】:

  • 对于两条不同的路径,它们是只有顶点 u 和 v 相同还是只是不同的路径(可能有共同的顶点)?
  • @wcochran Distinct 仅表示任何一对这样的路径至少在一条边上不同。
  • 大多数类似 Dijkstra 的算法将构建一棵树(以 u 为根)产生一条最短路径,您可以从 v 处的叶子向后走回到 u 处的根。您所问的问题是,可能存在大量组合的最小路径,仅枚举它们可能非常昂贵。
  • @wcochran 我可以使用一些广度优先搜索的实现吗?那会是什么样子?
  • BFS 会很好用。每次通过,您都会从根节点更深入地搜索 1 级。因此,如果您在第 n 次通过时遇到 v,您就知道最小长度为 n。然后,您可以计算在最后一次通过时遇到 v 的次数并退出。我会考虑一下细节。

标签: algorithm graph-theory


【解决方案1】:

广度优先搜索 (BFS) 的第 n 次迭代中,我们将所有顶点添加到 与根距离为 n 的队列。一旦我们遇到 目的节点,我们找到了最短距离,即 n。为了 跟踪我们需要维护最短计数的每个节点的所有可能路径的数量 我们走的路。在下图中,我们看到有 20 个可能的最短 从 u 到 v 的路径。当我们遇到一个未访问的节点时,我们总结了 它的前身路径。

该算法跟踪所有访问过的节点及其计数。每次迭代 访问一组新节点并更新它们的计数。 Q 持有所有节点 在上一次迭代中访问过,N 保存在中遇到的新节点 当前迭代。

for all w in V {  // initialize count's and visited
    count[w] = 0
    visited[w] = false
}

Q.insert(u)
count[u] = 1      // start node (one path from u to u)
visited[u] = true

while true { 

   N = empty      // N = find all nodes adjacent to nodes in Q
   while not Q.empty() {
       w = Q.remove()
       for x adjacent to w {
            if not visited[x] {
               count[x] = count[x] + count[w] // found another path
               N.insert(x)
            }
       }
   }

   if N.empty() return 0  // never reached v

   if v in N() return count[v]  // reached v : return count

   remove duplicates in N  // avoid double counting in next iteration

   for w in N              // mark all nodes in N as visited
       visited[w] = true;

    Q = N                  // prepare for next iteration
}

【讨论】:

  • 这仍然在线性时间内运行吗?我有点担心我们“删除 N 中的重复项”|V| 的情况。次,因为“while True”条件语句。在内部 while 循环之后,但在外部 while 循环结束之前,几乎所有其他事情都是一样的;他们不是都必须在|V|中运行吗?次,在最坏的情况下?
  • 编辑:或者“while True”是一条始终在恒定时间内运行的语句,特别是从不依赖于输入大小 |V|在算法的执行中?
  • 运行时间为 O(|V| + |E|)。通常 |E| > |V|所以在典型情况下 O(|V| + |E|) = O(|E|) 并且队列的深度决定了运行时间。对于完全连通的图,外循环只循环一次。对于弱连接图(例如很少的边),外部循环将被更频繁地采用,但队列将非常浅。如果 |E|
  • 所以内部 while 循环之外的所有行都不会重复执行一次?例如,“for w in N”;在一次迭代中,在最坏的情况下,这将花费 |V|-1 次循环通过大小 N(就像如果 w 与图的其余顶点相邻,并且所有这些相邻节点都添加到 N)。但是,当我们谈论 BFS 时,我们正在运行多次迭代。那么循环的运行时间不会变成O(但是多少次迭代*(|V|-1))?内部while循环下的其他行相同吗?编辑:我想它只会运行一个迭代器。现在我想起来了。其他行呢?
  • 删除重复项特别麻烦,如果 N 在最坏的情况下具有 |V|-1 个元素,并且我们希望将其重复项删除到一个新数组中,我们将不得不比较每个将新数组的元素添加到从 N 添加的元素中,因为我们正在将其添加到这个新数组中。对我来说,这可能会产生在 O(|V|^2) 中运行的效果。
【解决方案2】:

我认为您正在寻找这样的东西:

注意:图存储为邻接表

public int shortestPath(int u, int v, ArrayList<Integer>[] adj) {
    Queue<Integer> bfs = new ArrayDeque();
    bfs.add(u);

    int ret = 0;

    boolean[] visited = new boolean[adj.length];

    while (!bfs.isEmpty() && (ret == 0 || bfs.peek() == v)) {
        int node = bfs.poll();
        visited[node] = true;

        if (node == v) {
            ret++;
        }

        for (int next : adj[node]) {
            if (!visited[next]) {
                bfs.add(next);
            }
        }
    }
    return ret;
}

这些想法在 cmets 中,但本质上你正在运行一个 bfs,并跟踪当第一个路径访问 v 时有多少路径访问 v。请注意我是如何将节点设置为仅在我从队列中轮询它之后才访问的,这是计算路径数所需要的。

如果您仍然感到困惑,请告诉我

【讨论】:

  • BFS 通过 BFS 遇到目标节点的次数通常不会产生从 u 到 v 的唯一路径的数量。还需要添加到所有先前节点的所有唯一路径。
  • 您是在评论我的代码还是一般评论?因为我的代码已经这样做了
  • 不错。它确实在我的示例图中返回 20。不清楚它是如何计算所有路径的,但确实如此。
  • while 循环执行了 69 次——我想这是来自所有生成的路径(和非路径)。
  • 非路径?另外,您使用了哪些测试数据产生了 69 次迭代?
猜你喜欢
  • 2012-04-30
  • 2011-06-26
  • 1970-01-01
  • 1970-01-01
  • 2019-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多