De acordo com o Lema 22.11 de Cormen et al., Introdução aos algoritmos (CLRS):
Um gráfico direcionado G é acíclico se, e somente se, uma pesquisa profunda de G não produzir arestas posteriores.
Isso foi mencionado em várias respostas; aqui também fornecerei um exemplo de código baseado no capítulo 22 do CLRS. O gráfico de exemplo é ilustrado abaixo.
O pseudocódigo do CLRS para a pesquisa de profundidade é o seguinte:
No exemplo da Figura 22.4 do CLRS, o gráfico consiste em duas árvores DFS: uma que consiste nos nós u , v , x e y e a outra nos nós w e z . Cada árvore contém uma extremidade traseira: uma de x para ve outra de z para z (um auto-loop).
A principal conclusão é que uma borda traseira é encontrada quando, na DFS-VISIT
função, enquanto itera sobre os vizinhos v
de u
, um nó é encontrado com a GRAY
cor.
O código Python a seguir é uma adaptação do pseudocódigo do CLRS com uma if
cláusula adicionada que detecta ciclos:
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)
Observe que, neste exemplo, o time
pseudocódigo do CLRS não é capturado porque estamos interessados apenas em detectar ciclos. Há também algum código padrão para criar a representação da lista de adjacências de um gráfico a partir de uma lista de arestas.
Quando esse script é executado, ele imprime a seguinte saída:
Cycle detected: found a back edge from x to v.
Cycle detected: found a back edge from z to z.
Essas são exatamente as arestas traseiras no exemplo da Figura 22.4 do CLRS.