【问题标题】:How to find the number of different shortest paths between two vertices, in directed graph and with linear-time?如何在有向图中和线性时间中找到两个顶点之间不同最短路径的数量?
【发布时间】:2012-04-30 20:38:08
【问题描述】:

这是练习:

设 v 和 w 是有向图 G = (V, E) 中的两个顶点。设计一个线性时间算法来找出 v 和 w 之间的不同最短路径(不一定是顶点不相交)的数量。注意:G 中的边没有加权


对于这个消费税,我总结如下:

  1. 是有向图
  2. 它要求不同最短路径的数量。首先,路径应该是最短的,然后可能有多个这样的最短路径长度相同。
  3. 在 v 和 w 之间,因此从 v 到 w 和从 w 到 v 都应该计算在内。
  4. 线性时间。
  5. 图表未加权。

综合以上几点,我有如下想法:

  1. 我不需要使用Dijkstra’s Algorithm,因为该图没有加权,我们试图找到所有最短路径,而不仅仅是一条。
  2. 我为最短路径的数量维护一个 count
  3. 我想先从 v 使用 BFS 并维护一个 global level 信息
  4. 我每次将global level 增加一个,然后 BFS 达到一个新的水平
  5. 我还维护了shortest level 信息,以获取到 w 的最短路径
  6. 第一次在旅行中遇到w,我将global level分配给shortest levelcount++
  7. 只要global level等于shortest level,我每次再遇到w都会增加count
  8. 如果global level 变得比shortest level 大,我将终止旅行,因为我正在寻找最短路径而不是路径。
  9. 然后我再次为 w 到 v 执行 2 - 8

我的算法正确吗?如果我先做 v 到 w,然后再做 w 到 v,这仍然被认为是线性时间吗?

【问题讨论】:

    标签: algorithm data-structures graph breadth-first-search


    【解决方案1】:

    这里有一些想法。

    • 从 v->w 到节点 x 只能有多个最短路径,如果有多个路径通过同一顶点进入 x,或者如果在同一 DFS 级别多次遇到 x。

    证明:如果有多个路径通过同一个顶点进入x,显然有多个路径通过x。这很简单。现在让我们假设只有一种方式通过进入x 的每个顶点(最多)进入x

    如果之前遇到过 x,则当前路径都不能构成另一条最短路径。由于之前遇到过 x,所有可以遵循的路径都将至少比之前的最短路径长一个。因此,这些路径都不能对总和做出贡献。

    这意味着我们最多遇到每个节点一次并完成。所以正常的 BFS 就可以了。

    • 我们甚至不需要知道等级,只要遇到最终节点就可以得到最终数字。

    这可以编译成一个很简单的算法,主要就是BFS。

     - Mark nodes as visited as usual with BFS.
     - Instead of adding just nodes to the queue in the DFS add nodes plus number of incoming paths.
     - If a node that has been visited should be added ignore it.
     - If you find a node again, which is currently in the queue, do not add it again, instead add the counts together.
     - Propagate the counts on the queue when adding new nodes.
     - when you encounter the final, the number that is stored with it, is the number of possible paths.
    

    【讨论】:

    • 感谢您的解决方案,非常好。
    • 我必须标记qrqrq的回答,因为他首先指出了我的解决方案的缺陷并引起了你们之间的良好讨论。
    • 只是指出-@qrqrq 不正确-您的解决方案没有缺陷;在最坏的情况下也不需要指数时间。您只需要使用 BFS 算法的修改版本。
    • @DanNissenbaum:从我的回答中对 BFS 的一些简单修改的​​介绍中,第二部分(可能并且不需要 EXPTIME)应该是显而易见的。
    • @JacksonTale:我不确定您的解决方案是否会像 qrqrq 假设的那样存在缺陷。 qrqrq 的假设是基于手头任务的(假设的)不可能性,但他在这一点上是错误的。所以你的算法也可能有效。只需在他的图表上测试它,如果我正确理解您的算法,它不会像他假设的那样返回 2,但我不确定它是否正确,或者它是否仍然返回错误。
    【解决方案2】:

    您的算法在类似的图表上中断

      *   *   *   1
     / \ / \ / \ / \
    v   *   *   *   w
     \ / \ / \ / \ /
      *   *   *   2
    

    所有边从左到右。它计算了两条路径,一条通过1,另一条通过2,但12 都可以通过八条不同的最短路径从v 到达,总共十六条。

    【讨论】:

    • 我认为你是对的。如何修改我的算法,在 BFS 时删除discovered 的统计信息,而是使用processed 检查我是否通过顶点?
    • 按照这些思路的正确算法在最坏的情况下需要指数时间——每个新的恒定大小“褶皱”会使要计算的路径数量加倍。
    • 实际上,我认为可以通过将“成功计数”与跟踪该节点下方成功路径数量及其长度的每个节点相关联来避免指数时间。修改后的 BFS 算法在递归退出时更新成功计数(将成功计数向上传递)。当 BFS 算法命中一个先前被命中的节点时(此命中是 BFS 算法的一部分),它会避免递归,并将成功计数结果简单地传递回链上。
    • @qrqrq:仅当您想实际输出所有最短路径时才成立。如果您只需要数字,我认为可以这样做,因为您不必遵循所有最短路径。
    • @qrqrq:也许“正确的算法在最坏的情况下需要指数时间”这句话对你的意思产生了错误的印象。因为沿着这些思路的正确算法(即修改的 bfs)不需要 EXPTIME。一个简单的修改 bfs 可以在 O(N) 中正确地做到这一点。但是可能不清楚,什么应该被认为是“沿着这些思路的正确算法”。
    【解决方案3】:

    正如 qrqrq 所示,您的算法在某些图表上失败了,但 BFS 的想法很好。相反,维护一个大小为|V| 的数组z,您将其初始化为零;在z[i] 中保持距离小于level 的已发现顶点i 的最短路径数。还要维护一个大小为|V| 的数组d,如果该距离小于level,则d[i] 是从v 到顶点i 的距离。将level初始化为0,d[v]初始化为0,z[v]初始化为1(从vv有一条长度为0的路径),并将d的所有其他条目设置为@987654338 @ 和 z0

    现在,当您在 BFS 中遇到从 ij 的边时,那么:

    • 如果d[j] = -1,则设置d[j] := levelz[j] := z[i]
    • 如果d[j] = level,则设置z[j] := z[j] + z[i]
    • 否则,什么也不做。

    原因是对于从vi 的每一条最短路径,都有一条从vj 的最短路径。这将给出从v 到每个顶点在线性时间内的最短路径数。现在再次执行相同操作,但从 w 开始。

    【讨论】:

    • 如何修改我的算法,当 BFS 时,我删除发现的统计信息,而不是使用处理来检查我是否通过顶点?
    • @JacksonTale 不确定我理解。您肯定需要维护您遇到的每个顶点的最短路径数 - 否则您将如何区分在同一级别发现的具有不同最短路径数的不同顶点?
    • @ErikP.:有一个更简单的解决方案......请参阅上面的答案。
    • @LiKao - 让提问者决定哪个解决方案更简单可能会更好,即使在很明显的情况下也是如此。这样就有点谦虚了。
    • @DanNissenbaum 我知道你很善良,但我更喜欢李考的方式,哈哈
    【解决方案4】:

    这个算法在我看来是正确的。

    如您所知,BFS 是线性时间 (O(N)) 搜索,因为完成它所需的时间 T 最坏的情况是 T = C + a * N,其中 N 是节点数,@987654325 @、a 是任何固定常量。

    在您的情况下,执行两次搜索 - 首先从 vw,然后从 wv - (在最坏的情况下)是 2T2C + 2a * N,这也满足如果你定义了一个新的C' = 2C和一个新的a' = 2a,那么线性时间要求O(N),因为C'a'也是固定常数。

    【讨论】:

    • 谢谢。一个问题:图问题中的线性时间也意味着 O(n)?不是一口气完成所有事情吗?
    • @JacksonTale 在任何算法中它通常意味着 O(n)。但是在图中有一个问题:n 可以表示顶点数或边数。
    • 丹,很抱歉将您的答案取消标记为正确答案。 qrqrq 在我的算法中发现了一个缺陷
    • 我没有详细介绍您将使用的 BFS 算法的性质,而是专注于线性时间问题。我相信 BFS 算法的一个版本足以避免多次递归,因此保持 BFS 算法的O(N) 性质,避免指数递归。见我上面的评论。我还没有对其进行编程以进行测试,但我认为它应该可以工作。
    【解决方案5】:
    int edgeCb( graphPT g, int x, int y )
    {
        if ( dist[ y ] > dist[ x ] + 1 ) {
            dist[ y ] = dist[ x ] + 1; // New way
            ways[ y ] = ways[ x ]; // As many ways as it's parent can be reached
        } else if ( dist[ y ] == dist[ x ] + 1 ) {
            ways[ y ] += ways[ x ]; // Another way
        } else {
            // We already found a way and that is the best
            assert( dist[ y ] < g->nv );
        }
        return 1;
    }
    

    上面的代码为我在这篇文章中提到的所有类型的图表提供了正确的结果。基本上是 BFS 遍历的边缘回调。

    dist[开始] = 0; 方式[开始] = 1;

    其余所有顶点 dist[ x ] = numberOfVertices; // 这超出了最大可能距离

    BFS(g, 开始);

    如果ways[end]不为零,则表示路径数,dist[end]表示最短距离。

    incaseways[ end ] == 0 表示无法从头到达终点。

    如果有任何漏洞,请告诉我。

    【讨论】:

      【解决方案6】:

      更改 BFS 的最简单解决方案:

      count(v) = 0, count(s) = 1. 对于 v 的每个邻居 u,如果 (d(v) + 1 == d(u)),则 count(u) += count(v )。现在重置所有内容并从末端顶点执行相同操作。

      【讨论】:

        【解决方案7】:

        这样可以吗

        1. 我使用 BFS 遍历直到到达目标顶点并保持关卡
        2. 一旦我到达目的地关卡,我会使用如下的关卡表

        从级别表中,我开始遍历计算路径中顶点的父节点的数量(第一次它将是目标顶点)。
        在每一步,我都会将在该特定级别找到的不同父节点的数量乘以到目标顶点的最短路径。
        我向上移动级别,只考虑落入我的路径的节点,并乘以在每个级别找到的 distinct parents 的数量,直到达到级别 0。

        这行得通吗?

        【讨论】:

          【解决方案8】:

          只需检查这里给出的很好的解释:

          https://www.geeksforgeeks.org/number-shortest-paths-unweighted-directed-graph/

          简单地说,我们可以修改任何最短路径算法, 当更新步骤到来时,增加一个计数器 当当前路径提议具有 直到那一刻为止找到的最短路径的长度相同。

          在特定情况下,当它是未加权或具有常数的图形时 所有边的权重,最简单的方法是修改 BFS。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-05-25
            • 2015-02-19
            • 2016-03-26
            • 1970-01-01
            相关资源
            最近更新 更多