Согласно лемме 22.11 Кормена и др., Введение в алгоритмы (CLRS):
Ориентированный граф G ацикличен тогда и только тогда, когда поиск G в глубину не дает обратных ребер.
Это было упомянуто в нескольких ответах; здесь я также приведу пример кода, основанного на главе 22 CLRS. Пример графика показан ниже.
Псевдокод CLRS для поиска в глубину гласит:
В примере на рисунке 22.4 в CLRS график состоит из двух деревьев DFS: одно состоит из узлов u , v , x и y , а другое - из узлов w и z . Каждое дерево содержит один задний край: один от x до v и другой от z до z (самоконтроль).
Ключ реализации является то , что задний край встречается , когда в DFS-VISIT
функции, в то время как итерация соседей v
из u
, узел встречается с GRAY
цветом.
Следующий код Python - это адаптация псевдокода CLRS с if
добавленным предложением, которое обнаруживает циклы:
import collections
class Graph(object):
def __init__(self, edges):
self.edges = edges
self.adj = Graph._build_adjacency_list(edges)
@staticmethod
def _build_adjacency_list(edges):
adj = collections.defaultdict(list)
for edge in edges:
adj[edge[0]].append(edge[1])
return adj
def dfs(G):
discovered = set()
finished = set()
for u in G.adj:
if u not in discovered and u not in finished:
discovered, finished = dfs_visit(G, u, discovered, finished)
def dfs_visit(G, u, discovered, finished):
discovered.add(u)
for v in G.adj[u]:
# Detect cycles
if v in discovered:
print(f"Cycle detected: found a back edge from {u} to {v}.")
# Recurse into DFS tree
if v not in finished:
dfs_visit(G, v, discovered, finished)
discovered.remove(u)
finished.add(u)
return discovered, finished
if __name__ == "__main__":
G = Graph([
('u', 'v'),
('u', 'x'),
('v', 'y'),
('w', 'y'),
('w', 'z'),
('x', 'v'),
('y', 'x'),
('z', 'z')])
dfs(G)
Обратите внимание, что в этом примере time
псевдокод в CLRS не фиксируется, потому что мы заинтересованы только в обнаружении циклов. Существует также некоторый шаблонный код для построения представления списка смежности графа из списка ребер.
Когда этот скрипт выполняется, он печатает следующий вывод:
Cycle detected: found a back edge from x to v.
Cycle detected: found a back edge from z to z.
Это именно задние края в примере в CLRS Рисунок 22.4.