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

时间:2017-05-02 18:35:31

标签: python algorithm data-structures graph

我有以下代码,它是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

无论我的图表是什么样子,它总是返回true,我无法弄清楚我做错了什么。当我在纸上跟踪它时算法有效,但实现并没有转化为工作代码。

2 个答案:

答案 0 :(得分:2)

对于无向图,您的算法不正确。您可以简单地检测一个周期,因为A和B之间的第一个边缘(B是A的邻居,A是B的邻居)。

答案 1 :(得分:2)

您的代码正在访问您刚刚来自的节点的邻居,因此您向前和向后移动相同的边缘,只是发现您已经访问过您实际来自的节点。但算法错误地认为这代表了一个循环。

因此,检查邻居是否已被访问是不够的。如果之前还没有相应的边缘,那只表示一个周期。

使算法工作的一种方法是将边存储在堆栈上,而不仅仅是节点。然后,您可以轻松检查邻居是否是边缘的另一端,然后忽略它:

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

请注意,您在问题中提供的图表有一个周期:

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

如果取出C和F之间的连接(例如),上面的代码将返回False