简体   繁体   中英

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.

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. 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).

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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