【问题标题】:need a graph algorithm similar to DFS需要类似DFS的图算法
【发布时间】:2010-02-24 00:23:47
【问题描述】:

我很好奇是否有一种特定的图算法通过选择起始节点然后通过 DFS 继续遍历未加权的无环有向图。如果遇到具有未搜索前任的节点,则它应该回溯传入路径,直到探索完所有要开始的路径。

我找到了wikipedia category for graph algorithms,但这里有一小部分算法,我对其中的大部分都不熟悉。

编辑:示例: 给定图 {AB, EB, BC, BD},遍历为:{A,B,E,B,C,D} 或唯一顺序为 {A,B,E,C,D}。 请注意,如果第一个起始节点的所有路径都用尽,则此算法与 BFS 或 DFS 不同,不需要从新的起始节点重新开始。

【问题讨论】:

  • @Samuel:DAG 不一定是树。考虑带有边 {AB, AC, BD, CD, DE} 的图。 DFS 会按照 {A, B, D, E, C} 的顺序访问它们,但他想要 {A, B, C, D, E}
  • @Poita_:你提到的例子似乎是一个简单的 BFS。
  • @Poita_ DFS 不仅适用于树。它也可以用于图表。在您的情况下,广度优先搜索可以解决问题(我在他的问题中看不到您的解释)
  • @Anon。是的,BFS 在那里工作,但这纯属巧合。 @塞缪尔。我并不是说 DFS 不能用于非树形图,我是从您的第一条评论中回答您的问题,即为什么 DFS 没有实现 OP 的目标。
  • @Poita_:差不多。我想要 {A,B,D,C,A,C,D,E}。回溯重新访问节点,但是为了讨论,只打印新节点 {A,B,D,C,E} 是可以的。我将使用类似的图表和解决方案更新问题。

标签: algorithm graph-theory directed-graph graph-traversal


【解决方案1】:

在 DFS 中,您通常根据从 u 开始的边来选择要在 u 之后访问的顶点。您想首先根据以 u 结尾的边进行选择。为此,您可以拥有transpose graph 信息,然后尝试先从那里获取顶点。

应该是这样的:

procedure dfs(vertex u)
  mark u as visited
  for each edge (v, u) //found in transpose graph
    if v not visited
      dfs(v)
  for each edge (u, w)
    if v not visited
      dfs(w)

【讨论】:

    【解决方案2】:

    您正在寻找的是拓扑排序。据我所知,没有任何预计算,没有简单的方法可以按照拓扑排序顺序遍历图。

    获取topsort的标准方法是做一个标准的DFS,然后按照访问时间的顺序存储访问过的节点。最后,反转这些节点,瞧,你按照你想要的顺序拥有它们。

    伪代码:

    list topsort
    
    procedure dfs(vertex u)
      mark u as visited
      for each edge (u, v)
        if v not visited
          dfs(v)
      add u to the back of topsort
    

    列表topsort 然后将包含您想要的相反顺序的顶点。只需反转 topsort 的元素即可纠正。

    【讨论】:

    • 我认为这可能是正确的。我会尽快查看并回复您。
    • Knuth 还发布了至少一种有趣的拓扑排序,如果您的边存储在数组中,这很有用。首先,您计算每个节点有多少前辈。那些为零的人排队。当您从该队列中提取项目时,您会减少后继者的前任计数,当计数达到零时将项目添加到队列中。有趣的是它的处理效率如何,但谷歌让我很烦——我找不到一个好的链接 ATM 来了解细节。
    【解决方案3】:

    如果你正在寻找topological sort,你也可以这样做,给定一个adjacency list(或一个边缘列表(u,v),你可以在O(E)时间预处理):

    list top_sort( graph in adjacency list )
         parent = new list
         queue = new queue
         for each u in nodes
             parent(u) = number of parents
             if ( parent(u) is 0 ) // nothing points to node i
                 queue.enqueue( u )
    
         while ( queue is not empty )
             u = queue.pop
             add u to visited
             for each edge ( u, v )
                 decrement parent(v) // children all have one less parent
                 if ( parent(v) is 0 )
                     queue.enqueue( v )
    

    给定一个adjacency list(或一个边列表(u,v)),这是O( V + E ),因为每个边被触摸两次——一次递增,一次递减,每次在O(1)时间。对于普通队列,每个顶点也最多会被队列处理两次 - 这也可以在 O(1) 中使用标准队列完成。

    请注意,这与 DFS(至少是直接实现)的不同之处在于它处理森林。

    另一个有趣的注意事项是,如果您将 queue 替换为施加某种结构/排序的 priority_queue,您实际上可以返回按某种顺序排序的结果。

    例如,对于一个规范的类依赖图(如果你选择了 Y 类,你只能选择 X 类):

    100:
    101: 100
    200: 100 101
    201: 
    202: 201
    

    结果你可能会得到:

    100, 201, 101, 202, 200
    

    但是如果你改变它,让你总是想先学习编号较低的课程,你可以很容易地把它改成返回:

    100, 101, 200, 201, 202
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-10
      • 2011-05-21
      • 2021-12-18
      • 1970-01-01
      相关资源
      最近更新 更多