使用 DFS 检查无向图中的循环?

Checking for a cycle in an undirected graph using DFS?

因此,我为 DFS 编写了以下代码:

void dfs (graph * mygraph, int foo, bool arr[]) // here, foo is the source vertex
{
    if (arr[foo] == true)
        return;
    else
    {
        cout<<foo<<"\t";
        arr[foo] = true;
        auto it = mygraph->edges[foo].begin();
        while (it != mygraph->edges[foo].end())
        {
            int k = *it;
            if (arr[k] == false)
            {
                //cout<<k<<"\n";
                dfs(mygraph,k,arr);
                //cout<<k<<"\t";
            }
            it++;
        }
    }
    //cout<<"\n";
}

现在,我在一个无向图中读到,如果在 DFS 时,它再次 returns 到同一个顶点,就有一个循环。所以,我的做法是这样的,

bool checkcycle( graph * mygraph, int foo, bool arr[] )
{

    bool result = false;
    if (arr[foo] == true)
    {
        result = true;
    }
    else
    {
        arr[foo] = true;
        auto it = mygraph->edges[foo].begin();
        while (it != mygraph->edges[foo].end())
        {
            int k = *it;
            result = checkcycle(mygraph,k,arr);     
            it++;
        }
    }
    return result;
}   

但是,我的 checkcycle 函数 returns 即使它们不是循环也是如此。这是为什么?我的功能有问题吗?没有执行问题,否则我会调试,但他们的逻辑似乎有问题。

在 checkcycle 开始之前,arr[] 中的所有布尔值都设置为 false 了吗?

你确定你的节点迭代器没有在它已经遍历的边上加倍返回(因此无论循环如何多次看到起始节点)?

请注意,您的函数并不像您认为的那样。让我尝试逐步了解这里发生的事情。假设以下关系:(1,2)、(1,3)、(2,3)。我不假设反射性(即 (1,2) 并不意味着 (2,1))。关系是定向的。

  1. 从节点 1 开始。将其标记为已访问
  2. 迭代其子项(2 和 3)
  3. 在节点2时,递归调用check cycle。此时 2 也被标记为已访问。
  4. 递归调用现在访问 3(深度搜索)。 3 也被标记为已访问
  5. 第 4 步的调用已返回 false
  6. 第 3 步的调用在返回时死亡 false
  7. 我们回到第 2 步。现在我们将迭代节点 3,它已在第 4 步中标记。它只是 returns true.

您需要一堆访问过的节点,或者您只搜索原始节点。堆栈也会检测子循环(不包括原始节点的循环),但它也需要更多的内存。

编辑:节点堆栈不仅仅是一堆true/false值,而是一堆节点号。如果某个节点存在于堆栈中,则该节点已在当前堆栈跟踪中被访问。

然而,有一种更利于记忆的方法:设置 arr[foo] = false; 为调用终止。像这样:

bool checkcycle( graph * mygraph, int foo, bool arr[], int previousFoo=-1 )
{
    bool result = false;
    if (arr[foo] == true)
    {
        result = true;
    }
    else
    {
        arr[foo] = true;
        auto it = mygraph->edges[foo].begin();
        while (it != mygraph->edges[foo].end())
        {
            int k = *it;

            // This should prevent going back to the previous node
            if (k != previousFoo) {
                result = checkcycle(mygraph,k,arr, foo);
            }

            it++;
        }
        
        // Add this
        arr[foo] = false;
    }
    return result;
}   

我觉得应该够了。

编辑:现在应该支持无向图。 节点:此代码未测试

编辑:有关更详细的解决方案,请参阅 Strongly Connected Components

编辑:尽管评论中给出了具体解决方案,但该答案已被市场接受。阅读评论了解详情。