如何检测无向图是否具有循环并使用BFS或DFS输出它

时间:2015-01-30 20:39:29

标签: algorithm graph cycle depth-first-search breadth-first-search

关于此的另一个问题仅回答了如何检测循环,而不是输出循环。因此,我想在无向图上编写一个在O(V + E)时间(V =顶点,E =边缘)运行BFS或DFS的算法,如果有一个循环,则输出循环。

到目前为止,我所知道的是BFS / DFS如何工作,如果您访问已经标记为已访问的节点,则可以使用BFS检测周期。

2 个答案:

答案 0 :(得分:4)

要使用DFS检测并输出循环,只需在到达时标记每个顶点;如果标记了当前顶点的任何子节点,则表示您有一个涉及该子节点的循环。此外,你知道那个子顶点是DFS遇到的属于这个特定周期的第一个顶点,并且自从它首次遇到该顶点以来DFS中的每一个移动(即从那时起的每个递归调用还没有返回)已经访问了循环中的另一个顶点。传递回调用堆栈所需的唯一信息是此子顶点,或指示未找到循环的特殊值。您可以将其作为返回值传递回来:

dfs(v, p) {
    marked[v] = true
    For each neighbour u of v:
        If u != p:   # I.e. we ignore the edge from our parent p
            If marked[u]:
                Append v to cycleVertices
                Return u   # Cycle!
            Else:
                result = dfs(u, v)
                If result == FINISHED:
                    # Some descendant found a cycle; now we're just exiting
                    Return FINISHED
                Else if result != NOCYCLE:
                    # We are in a cycle whose "top" vertex is result.
                    Append v to cycleVertices
                    If result == v:
                        return FINISHED   # This was the "top" cycle vertex
                    Else:
                        return result     # Pass back up

    marked[v] = false    # Not necessary, but (oddly?) not harmful either ;)
    Return NOCYCLE
}

为某些顶点dfs(r, nil)(以及任何非顶点值r)调用nil后,cycleVertices将填充一个循环(如果找到一个)。

[编辑:正如胡安·洛佩斯所指出的那样,没有标记的顶点是不必要的,并且可能令人困惑;但是,有趣的是,不会影响无向图的时间复杂度。]

答案 1 :(得分:1)

如果您正在使用DFS,则可以通过打印出节点名称来递归地执行此操作,具体取决于是否已访问过访问过的节点:

define function DFSVisit(node, cycle):
    if node.visited is true:
        push node.name to cycle
        return true
    else 
        set node.visited to true

    for each child of node:
        if DFSVisit(child, cycle) is true:
            set foundCycle to true
            break out of for loop

    if foundCycle is true:
        if (cycle.length <= 1 or cycle[first] != cycle[last]):
            push node.name to cycle
        return true
    else 
        return false

在它结束时,循环将包含在图中找到的循环,否则它将为空。另请注意,显示循环的顺序取决于您如何推动&#39;循环(按下将向后打印,推到前面将打印&#39;按顺序排列&#39;)

编辑:非常感谢@j_random_hacker帮助我调试我的伪代码!