【问题标题】:Graph Traversal using DFS使用 DFS 进行图遍历
【发布时间】:2017-02-12 07:09:49
【问题描述】:

我正在学习 Steven S. Skiena 的《算法设计手册》中的图遍历。在他的书中,他提供了使用 dfs 遍历图形的代码。下面是代码。

dfs(graph *g, int v)
{
       edgenode *p;
       int y;
       if (finished) return;
       discovered[v] = TRUE;
       time = time + 1;
       entry_time[v] = time;
       process_vertex_early(v);
       p = g->edges[v];
       while (p != NULL) {
            /* temporary pointer */
            /* successor vertex */
            /* allow for search termination */
            y = p->y;
            if (discovered[y] == FALSE) {
                parent[y] = v;
                process_edge(v,y);
                dfs(g,y);
            }
            else if ((!processed[y]) || (g->directed))
                 process_edge(v,y);
            }
            if (finished) return;
            p = p->next;
        }
        process_vertex_late(v);
        time = time + 1;
        exit_time[v] = time;
        processed[v] = TRUE;
}

在无向图中,看起来下面的代码正在处理边两次(调用方法 process_edge(v,y)。一次在遍历顶点 v 时,另一次在处理顶点 y 时)。
所以我在else if ((!processed[y]) || (g->directed)) 中添加了条件parent[v]!=y
它只处理一次边缘。但是,我不确定如何修改此代码以使用并行边缘和自循环边缘。代码应处理并行边缘和自循环。

【问题讨论】:

    标签: algorithm depth-first-search


    【解决方案1】:

    你是对的。

    引用book's (2nd edition) errata

    (*) 第 171 页,第 -2 行 -- dfs 代码有一个错误,其中每个树边缘 在无向图中被处理两次。测试必须是 实力雄厚:

    else if (((!processed[y]) && (parent[v]!=y)) || (g->directed))
    

    关于周期 - 请参阅 here

    【讨论】:

      【解决方案2】:

      简答:

      用您的(parent[v]!=y) 替换 (!processed[y]),而不是将其添加到条件中。

      详细解答:

      在我看来,书中所写的实现存在一个错误,您发现并修复了该错误(平行边除外。更多内容见下文)。该实现应该对有向图和无向图都是正确的,它们之间的区别记录在 g->directed 布尔属性中。

      在书中,作者在实现之前写道:

      深度优先搜索的另一个重要特性是它将 无向图的边恰好分为两类:树边和后边。这 树边发现新的顶点,并且是在父关系中编码的那些。后退 边是那些其另一个端点是正在扩展的顶点的祖先的那些, 所以他们重新指向树。

      所以条件(!processed[y]) 应该处理无向图(因为条件(g->directed) 是处理有向图)通过允许算法处理作为后边的边并防止它重新处理那些是树的边缘(在相反的方向)。但是,正如您所注意到的,当通过这种条件读取子项时,树边缘被视为后边缘,因此您应该用建议的 (parent[v]!=y) 替换此条件。

      只要没有平行边,当算法读取无向图时,条件(!processed[y]) 总是为真(进一步详细说明为什么这是真的 - *)。如果有平行边 - 在它们的第一个“副本”之后读取的那些平行边将产生 false 并且边将不会被处理,当它应该被处理时。但是,您建议的条件将区分树边和其余边(后边、平行边和自环),并允许算法仅处理那些不是相反方向的树边。

      要引用自边,它们应该适用于新旧条件:它们是带有y==v 的边。找到他们,y 被发现(因为v 在通过其边缘之前被发现),未处理(v 仅作为最后一行处理 - 在通过其边缘之后)并且它不是v的父级(v 不是它自己的父级)。


      *通过v 的边缘,算法读取已发现的y 的条件(因此它不会进入第一个条件块)。正如上面所引用的(书中也有一个半证明,我将在本脚注的末尾加入),p 要么是树边缘,要么是后边缘。当y 被发现时,它不能是从vy 的树边。它可以是祖先的后沿,这意味着调用是在某个点开始处理这个祖先的递归调用中,因此祖先的调用尚未到达最后一行,将其标记为已处理(因此它仍然被标记未处理),它可以是从yv 的树边,在这种情况下,同样的情况成立 - 并且y 仍被标记为未处理。

      每条边是树边或后边的半证明:

      为什么边不能去兄弟或表亲节点而不是祖先? 从给定顶点 v 可到达的所有节点在我们完成 从 v 遍历,所以这种拓扑对于无向图是不可能的。

      【讨论】:

      • 条件(!processed[y]) 永远为真也是我的结论!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-18
      • 2023-01-14
      • 1970-01-01
      • 2021-02-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多