在图中,如何计算节点可以有效达到的所有节点的总和?

时间:2011-07-22 09:12:42

标签: algorithm graph

给定有向图,将权重分配给每个节点 从任何节点A开始,将有一组可以从A到达的节点 将SUM定义为此集合的总权重。

问题: 如何有效地计算该图中每个节点的SUM? 该图可能包含数百万个节点。

更新1:
图数据结构由一组起始节点组成,每个节点都有一组指向节点。

我尝试了什么:
递归计算每个节点的后代,并根据后代集计算SUM。这种递归效率非常低,因为我必须多次设置union,uteration操作。此外,我试图在每个节点缓存后代集。但是,它很容易耗尽内存 那么,还有其他解决方案吗?

更新2:
示例图,边缘全部是定向的,上部节点指向下部节点
Sample Graph
结果应该是
    SUM(1)= 55     SUM(2)= 35     SUM(3)= 41     SUM(4)= 19     SUM(5)= 22     SUM(6)= 25     ...

2 个答案:

答案 0 :(得分:10)

找到图表的SCC(Tarjan的算法,或双DFS运行)。

对于每个SCC计算它的节点权重之和,用PARTIAL-SUM表示该值。

以反向拓扑顺序迭代SCC;对于每个SCC中的每个节点,其SUM将是相邻SCC的所有SUM值加上它自己的PARTIAL-SUM值的总和。

线性运行时间O(E+V)因为找到SCC是线性的,拓扑排序是线性的,并且求和是线性的,因为我们最多访问每个SCC一次,每个分支最多一次。

修改

正如tzkuzzy并行路径的评论中所指出的那样构成问题。通过SCC图上的简单DFS运行可以轻松解决这个问题。在任何跨边缘上,我们只需将已访问过的节点放在DFS树上,直到我们到达未完全搜索的父节点,这对节点(底部访问的节点和祖先)之间有两条不同的路径,我们为这些后代的每个节点创建一个列表,并且总和只是减去它们的PARTIAL-SUM值。

所以如果:

 u
/ \
\ /
 w

我们的DFS会选择从uw的子节点连接的交叉边缘,并追溯到u(对于熟悉学校教授的典型DFS的人来说)最简单的解释是,u被定义为w的第一个灰色祖先,因此我们将w添加到我们在u上维护的列表中。

然后,当我们按照描述对每个SCC的相邻SCC求和时,我们添加一个额外的步骤,我们遍历所提到的列表并简单地减去PARTIAL-SUM值。

DFS本身仍然是线性的。如果我们缓存结果,那么从节点到祖先的回溯可以是线性的(这样我们不会多次遍历同一个边缘)。总和中的额外工作最多为O(V),因此我们没有更改运行时间。

修改

包含 - 排除使得这比我最初的想法更难。此解决方案不完整,不起作用。每个节点的简单BFS更昂贵,但更容易实现。

答案 1 :(得分:0)

我以前的回答是个好主意但是包含 - 排除交叉子树很难解决(我认为你的递归方法也会受到影响)。想一想,我开始怀疑在这个问题中是否存在最优的子结构,这将导致动态编程解决方案...

我将提出一个更简单(虽然效率更低)的解决方案:

对于每个节点,运行BFS / DFS并对遇到的所有节点的值求和。它将在O(V)空间和O(V(E+V)) = O(EV)时间运行,但 super 很容易实现,不需要递归。

您可以通过查找SCC图进行优化,而不是在每个节点上执行BFS / DFS,对每个SCC执行一次。如果图表是“cliquey”,这可能会增加一个巨大的运行时间。