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

时间:2015-07-20 21:00:23

标签: c++ graph depth-first-search

所以,我为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时,它再次返回到同一个顶点,则有一个循环。因此,我所做的就是这个,

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函数也会返回true。这是为什么?我的功能有问题吗?没有执行问题,否则我会调试,但他们的逻辑似乎有些不对劲。

2 个答案:

答案 0 :(得分:2)

请注意,您的功能并不像您认为的那样完成。让我试着逐步了解这里发生的事情。假设以下关系:(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中标记。它只返回true
  8. 您需要一堆访问过的节点,或者只搜索原始节点。堆栈也将检测子循环(不包括原始节点的循环),但它也需要更多内存。

    编辑:节点堆栈不仅仅是一堆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

    编辑:尽管在评论中给出了具体的解决方案,但这个答案仍然被市场所接受。阅读评论以获取详细信息。

答案 1 :(得分:0)

在检查周期开始之前,arr []中的所有bool都设置为false吗?

你确定节点的迭代器没有加倍回到它已遍历的边缘(因此无论周期如何都会多次看到起始节点)?