我有以下代码,它是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,我无法弄清楚我做错了什么。当我在纸上跟踪它时算法有效,但实现并没有转化为工作代码。
答案 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
。