将循环图分解为最小数量的最短路径子图

时间:2011-08-25 19:34:47

标签: algorithm graph shortest-path

给定一个循环图,我正在寻找一种算法,将该图分解为非循环子图。这些子图中的每一个都具有根顶点,其中该顶点是计算最短路径的源。例如,给定下面的循环图,其中循环在3,4和5之间:

                      +-------------------------+
                       |                         |
                       |                         |
            +----------+----------+              |
            v          |          v              |
+---+     +---+      +---+      +---+     +---+  |
| 1 | --> | 3 | <--> | 4 | <--> | 5 | --> | 6 |  |
+---+     +---+      +---+      +---+     +---+  |
                       ^                         |
                       |                         |
                       |                         |
                     +---+                       |
                     | 2 |                       |
                     +---+                       |
                     +---+                       |
                     | 7 | <---------------------+
                     +---+

关于1的最短路径子图将是:

+---+     +---+     +---+     +---+
| 1 | --> | 3 | --> | 4 | --> | 7 |
+---+     +---+     +---+     +---+
            |
            |
            v
          +---+     +---+
          | 5 | --> | 6 |
          +---+     +---+

关于2的最短路径子图将是:

          +---+
          | 7 |
          +---+
            ^
            |
            |
+---+     +---+     +---+     +---+
| 2 | --> | 4 | --> | 5 | --> | 6 |
+---+     +---+     +---+     +---+
            |
            |
            v
          +---+
          | 3 |
          +---+

关于5的最短路径指标将是:

+---+     +---+     +---+     +---+
| 6 | <-- | 5 | --> | 4 | --> | 7 |
+---+     +---+     +---+     +---+
            |
            |
            v
          +---+
          | 3 |
          +---+

请注意,相对于3的最短路径子图是1的子集,4是2的子集。 6和7是叶子。

我当前(天真)的解决方案是为每个节点执行BFS,将节点标记为已访问以防止循环。然后检查子图是否是彼此的子集以创建最小数量的不同子图。有关更好,更正式的解决方案的任何想法吗?

编辑我的情况中的图表没有加权,但为子孙后代提供一般解决方案很好。

(使用http://bloodgate.com/graph-demo制作的图表)

2 个答案:

答案 0 :(得分:3)

您在上面列出的每棵树都称为 shortest path tree 要查找以某个顶点为根的单个最短路径树,您可以使用Dijkstra算法,它们都找到最短路径从源节点到每个其他节点,并显示哪些边用于到达那些节点。这会立即为您提供单个节点的最短路径树,假设图形不包含任何负边缘。如果要列出所有树,可以通过从每个节点开始运行Dijkstra算法来实现。给定Fibonacci堆,它在O(VE + V 2 log V)中运行,这非常快。

如果您没有Fibonacci堆,或者使用密集图,或者可能有负循环,则可能需要使用Floyd-Warshall算法。该算法在时间O(V 3 )下运行,并为每个节点计算到每个其他节点的最短路径,即使存在负边缘。从这里,您可以很容易地恢复所有最短的路径树。

希望这有帮助!

编辑:如果您的图表未加权,那么显然有一个非常好的算法来解决这个问题,该问题在时间O(M(n)log n)中运行,其中M(n)是将小数矩阵相乘所需的时间。由于这可以很快完成(大约O(n 2.3 ),这将是解决问题的最佳算法。有一篇论文关于算法 here ,但它是在付费墙后面。实际上,除非你正在处理真正巨大的图形(比如数万个节点),我认为你不必担心使用这个更强大的算法,但它仍然是好的了解。

答案 1 :(得分:2)

templatetypedef,OP使用“BFS”表示图表未加权。

这是一个算法,它与时间成比例地计算,对于最终集合中的每个根,可以从该根可到达的子图的大小。例如,对于有界度的图,这是输出大小的顺序。

为了唯一性,我将假设“最短路径”在长度 - 词汇顺序中意味着最少。在高级别,我们计算处理顶点的顺序,以便如果顶点u的BFS树包含顶点v,则u在v之前排序。每个顶点在线性时间内处理,包括确定它包含的顶点。

通过查找强组件,拓扑排序强组件,然后任意排序每个单个组件中的顶点来计算顺序。显然,只有当从u可到达的顶点集合是从v可到达的顶点的正确超集时,u才包含v。

要处理顶点u,从u计算BFS树,然后确定其子树没有离开子树的弧的顶点集 - 这些是被包含的子树。通过遍历树深度优先确定后者,为每个顶点v记录一个间隔I(v),其左端点是入口时间,右端点是出口时间。对于每个顶点v,计算包含I(v)的最小间隔J(v)和具有弧v-> w的所有I(w)。使用DFS,为每个顶点v计算包含v(v)的所有后代w的最小间隔K(v)。当且仅当K(v)= I(v)时,包含除u之外的顶点v

为什么要这样做?我们知道根据u的树的v子树是植根于v的树的子集。假设你包含v(换句话说,这两棵树是相等的)。然后很明显,来自v子树的每个弧的头部都会转到v的子树,否则,头部应该被探索过。相反,假设你不包含v。以v为根的树包含一个不在v的子树中的顶点,因此有一个弧离开v的子树。

我希望这个描述对你有用,但我担心你的实际问题涉及快速的点对点最短路径查询和子二次空间,为此可能有更好的方法。