简体   繁体   English

使用DFS检测无向图中的循环

[英]Detecting a cycle in an undirected graph using DFS

I have the following code which is a modification of DFS that detects whether an undirected graph has a cycle. 我有以下代码,它是对DFS的修改,用于检测无向图是否具有循环。

graph = {
    'A' : set(['B', 'C']),
    'B' : set(['D', 'E', 'A']),
    'C' : set(['A', 'F']),
    'D' : set(['B']),
    'E' : set(['B', 'F']),
    'F' : set(['C','E'])
}

def find_cycle(graph, start):
    colors = { node : "WHITE" for node in graph }
    colors[start] = "GRAY"
    stack = [start]
    while stack:
        for neighbor in graph[stack[-1]]:
            if colors[neighbor] == "GRAY":
                return True
            elif colors[neighbor] == "WHITE":
                colors[neighbor] = "GRAY"
                stack.append(neighbor)
            else:
                colors[neighbor] = "BLACK"
                stack.pop()
     return False

No matter what my graph looks like, it always returns true and I can't figure out what i'm doing wrong. 无论我的图是什么样,它总是返回true,而我无法弄清楚我在做什么错。 The algorithm works when I trace it on paper but the implementation doesn't translate to working code. 当我在纸上跟踪该算法时,该算法有效,但实现方式无法转换为有效代码。

Your algorithm is not correct for an undirected graph. 您的算法不适用于无向图。 You're simple going to detect a cycle as the very first edge between A and B (B is a neighbor of A and A is a neighbor of B). 您很容易将周期检测为A和B之间的第一个边缘(B是A的邻居,A是B的邻居)。

Your code is visiting as neighbor the node you just came from, and so you travel the same edge forward and backward, only to find you already visited the node you actually came from. 您的代码以邻居的身份访问了您刚来自的节点,因此您在同一边上来回移动,却发现您已经访问了您实际来自的节点。 But the algorithm wrongly concludes that this represents a cycle. 但是该算法错误地得出结论,这代表一个循环。

So it is not enough to check whether a neighbor has already been visited. 因此,仅检查邻居是否已被访问还不够。 That only represents a cycle if the corresponding edge had not yet been travelled before. 如果相应的边缘之前尚未移动过,则仅表示一个循环。

One way to make the algorithm work, is to store the edge on the stack, not just a node. 使算法起作用的一种方法是将边缘存储在堆栈中,而不仅仅是节点。 Then you can easily check if a neighbor is the other end of the edge, and then just ignore it: 然后,您可以轻松地检查邻居是否在边缘的另一端,然后忽略它:

def find_cycle(graph, start):
    colors = { node : "WHITE" for node in graph }
    colors[start] = "GRAY"
    stack = [(None, start)] # store edge, but at this point we have not visited one
    while stack:
        (prev, node) = stack.pop()  # get stored edge
        for neighbor in graph[node]:
            if neighbor == prev:
                pass # don't travel back along the same edge we came from
            elif colors[neighbor] == "GRAY":
                return True
            else: # can't be anything else than WHITE...
                colors[neighbor] = "GRAY"
                stack.append((node, neighbor)) # push edge on stack
    return False

Note that the graph you presented in the question has a cycle: 请注意,您在问题中显示的图形具有一个循环:

   A---C 
  /     \
 B---E---F

If you take out the connection between C and F (for example), the above code will return False . 例如,如果删除C和F之间的连接,则上面的代码将返回False

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM