【问题标题】:How to count all reachable nodes in a directed graph?如何计算有向图中的所有可达节点?
【发布时间】:2018-01-22 19:45:55
【问题描述】:

有一个有向图(可能包含循环),每个节点都有一个值,我们如何获得每个节点的可达值之和。例如,在下图中:

节点 1 的可达和为:2 + 3 + 4 + 5 + 6 + 7 = 27

节点 2 的可达和为:4 + 5 + 6 + 7 = 22

.....

我的解决方案:要得到所有节点的总和,我认为时间复杂度是O(n + m),n是节点数,m代表边数。应该使用DFS,对于每个节点我们应该使用递归的方法找到它的子节点,并在完成计算时保存子节点的总和,这样以后我们就不需要再计算了。需要为每个节点创建一个集合,避免循环导致的无休止计算。

有效吗?我认为它不够优雅,尤其是必须创建许多集合。有没有更好的解决方案?谢谢。

【问题讨论】:

标签: algorithm graph directed-graph


【解决方案1】:

这可以通过首先找到Strongly Connected Components (SCC) 来完成,这可以在O(|V|+|E|) 中完成。然后,为 SCC 构建一个新图 G'(每个 SCC 是图中的一个节点),其中每个节点的值是该 SCC 中节点的总和。

正式地,

G' = (V',E')
Where V' = {U1, U2, ..., Uk | U_i is a SCC of the graph G}
E' = {(U_i,U_j) | there is node u_i in U_i and u_j in U_j such that (u_i,u_j) is in E }

那么,这个图(G')就是一个DAG,问题就变得简单了,好像是question linked in comments的变种。

EDIT 以前的答案(删除)从这一点来看是一个错误,用新答案进行编辑。对此感到抱歉。

现在,可以从每个节点使用 DFS 来查找值的总和:

DFS(v):
  if v.visited:
    return 0
  if v is leaf:
    return v.value
  v.visited = true
  return sum([DFS(u) for u in v.children])
  • 这是 O(V^2 + VE) 最差的花瓶,但由于图中的节点较少,V 和 E 现在显着降低。
  • 可以进行一些局部优化,例如,如果一个节点有一个子节点,您可以重用预先计算的值,并且不再对子节点应用 DFS,因为在这种情况下不用担心计算两次。

这个问题的DP解决方案(DAG)可以是:

D[i] = value(i) + sum {D[j] | (i,j) is an edge in G' }

这可以以线性时间计算(在 DAG 的topological sort 之后)。

伪代码:

  1. 查找 SCC
  2. 构建 G'
  3. 拓扑排序 G'
  4. 为 G' 中的每个节点找到 D[i]
  5. 为每个 U_i 中的所有节点 u_i 应用值。

总时间为O(|V|+|E|)

【讨论】:

  • 这个 DP 解决方案不会重复计算具有多个路径的节点吗?
  • 这和图的传递闭包有什么区别吗? ——这在这次 AFIK 中无法计算,但对于我的一生,我就是想不通这有什么,而这没有。
  • @luqui 是的,你是对的。在这种情况下,这将对这些节点进行两次计数。
  • 感谢 cmets。它以某种方式跳过了我的注意力来修复它。这是一个小修复。比我希望的要少得多。我仍然希望有一种方法可以重新使用预先计算的数据,但目前还不确定如何使用。我仍然认为事先使用 SCC 可能是一个好主意。由于 SCC 是线性的,因此保存恒定数量的节点就足够了,这在运行二次跟踪算法时会得到回报。
【解决方案2】:

您可以使用DFSBFS 算法来解决您的问题。 两者都有复杂性O(V + E)

您不必计算所有节点的所有值。而且你不需要递归。 做这样的东西。

DFS 通常看起来像这样。

unmark all vertices
choose some starting vertex x
mark x
list L = x
while L nonempty
    choose some vertex v from front of list
    visit v
    for each unmarked neighbor w
        mark w
        add it to end of list

在您的情况下,您必须添加一些行

unmark all vertices
choose some starting vertex x
mark x
list L = x
float sum = 0
while L nonempty
    choose some vertex v from front of list
    visit v
    sum += v->value
    for each unmarked neighbor w
        mark w
        add it to end of list

【讨论】:

  • 请注意,这个解决方案是O(|V|+|E|)对于单个节点。如果你想从每个顶点找到所有可达节点的总和,你需要为每个顶点运行它再次节点,给出O(|V|^2 + |E||V|) 的总时间(这是问题所要求的)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多