【问题标题】:Finding Cycles in Graphs Using Depth-First Search使用深度优先搜索在图中查找循环
【发布时间】:2013-08-25 11:12:35
【问题描述】:

鉴于以下深度优先搜索,为什么 ProcessEdge 方法中的检查 if(Parent[currVertex] != successorVertex) 会检测到循环?此代码遵循 S.Skiena 的 Algortim Design Manual 一书中给出的算法。支票可能是错字,应该是if(Parent[successorVertex] != currVertex)。请要求任何澄清。我真的被困在这个问题上。

    public void Search(int start)
    {
        /* NOTE: the differences from BFS are: this uses a stack instead of a queue AND this maintains 'time' variable */
        Stack<int> s = new Stack<int>();
        int currVertex;
        int successorVertex;
        int time = 0;

        s.Push(start);

        Discovered[start] = true;

        while (s.Count != 0)
        {
            currVertex = s.Pop();
            // time increments every time we enter a node (when discovered) and every time we exit a node (when processed_late, i.e. when all its neighbours have been processed)
            time++;
            EntryTime[currVertex] = time;

            ProcessVertexEarly(currVertex);
            Processed[currVertex] = true;

            for (int i = 0; i < Graph.Vertices[currVertex].Count; i++)
            {
                successorVertex = Graph.Vertices[currVertex][i].Y;
                if (!Processed[successorVertex] || Graph.IsDirected)
                {
                    ProcessEdge(currVertex, successorVertex);
                }
                if (!Discovered[successorVertex])
                {
                    s.Push(successorVertex);
                    Discovered[successorVertex] = true;
                    Parent[successorVertex] = currVertex;
                }
            }
            // time increments every time we enter a node (when discovered) and every time we exit a node (when processed_late, i.e. when all its neighbours have been processed)
            time++;
            ExitTime[currVertex] = time;
            ProcessVertexLate(currVertex);
        }
    }

    private void ProcessEdge(int currVertex, int successorVertex)
    {
        if(Parent[currVertex] != successorVertex) // then we've found a cycle
        {
            /* Found cycle*/
        }
    }

更新

在勘误表http://www.cs.sunysb.edu/~skiena/algorist/book/errata 中找到此代码的更正。请参阅 (*) 第 173 页,process_edge 程序——正确的测试应该是

if (discovered[y] && (parent[x] != y)) { /* found back edge */

但这会检测周期吗? if 检查永远不会通过,因为在 DFS 方法中,process_edge 仅在 discovered[y] == false 时被调用。

【问题讨论】:

  • 回复您的更新:如果y 未被发现或未处理,Skiena 的dfs 将调用process_edge。在探索离开它们的每条边之前,不会将顶点标记为已处理。

标签: algorithm graph depth-first-search


【解决方案1】:

您发布的代码与 Skiena 的原始代码存在显着差异:bfs-dfs.c 和 findcycle.c 和 the rest。 Skiena 的代码有问题(试试图 3 2 1 2 2 3,一条两条边路径),所以也许将它音译成 Java 的人尝试了一些修复。不幸的是,修复后的版本似乎也有问题,但如果没有完整的程序,我无法确定。

我相信您突出显示的那行的意图如下。对于无向图中的深度优先搜索,有两种类型的边,tree和back。当且仅当存在后边时,该图才具有循环。现在,Skiena 选择的无向图表示是将每个无向边存储为两个有向弧,每个方向一个。如果我们使用深度优先搜索来检测这个有向图中的循环,那么由对应于单个无向边的两条有向弧组成的长度为 2 的循环将错误地报告为循环。如所写,检查确保候选后弧 y-&gt;x 不是树弧 x-&gt;y 的反向。

【讨论】:

  • 谢谢。你上面提到的图是不是要解释为:顶点0连接到顶点3,顶点1连接到顶点2,顶点2连接到顶点1等等?
  • 另外,你能推荐一种更好的存储图表的方法吗?谢谢
  • @bytefire 3 个顶点,2 个边,第一个边 1-2,第二个边 2-3。 Skiena 代码的问题在于它在树的边缘调用了process_edge。
  • @bytefire Skiena 的表示是存储简单无向图的好方法。
  • 我现在明白了!在没有环的无向图中,任何顶点至多有一个父节点。在第二轮处理边时(即反向弧),如果起始顶点的父节点与结束顶点不同,则我们有一个循环。谢谢你的帮助。顺便说一句,我将代码从 C 翻译为 C#,但没有使用书中的递归版本。我用堆栈替换了 BFS 代码中的队列。还没有测试,所以可能有其他错误。
猜你喜欢
  • 2012-01-17
  • 2012-10-15
  • 2022-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-31
相关资源
最近更新 更多