Selon le lemme 22.11 de Cormen et al., Introduction to Algorithms (CLRS):
Un graphe orienté G est acyclique si et seulement si une recherche en profondeur d'abord de G ne donne aucun bord arrière.
Cela a été mentionné dans plusieurs réponses; ici, je vais également fournir un exemple de code basé sur le chapitre 22 de CLRS. Le graphique d'exemple est illustré ci-dessous.
Le pseudo-code de CLRS pour la recherche en profondeur d'abord se lit comme suit:
Dans l'exemple de la figure 22.4 du CLRS, le graphique se compose de deux arbres DFS: l'un composé des nœuds u , v , x et y et l'autre des nœuds w et z . Chaque arbre contient un bord arrière: un de x à v et un autre de z à z (une auto-boucle).
La réalisation clé est qu'un bord arrière est rencontré lorsque, dans la DFS-VISIT
fonction, tout en itérant sur les voisins v
de u
, un nœud est rencontré avec la GRAY
couleur.
Le code Python suivant est une adaptation du pseudocode de CLRS avec une if
clause ajoutée qui détecte les cycles:
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)
Notez que dans cet exemple, le time
pseudocode de CLRS n'est pas capturé car nous ne sommes intéressés que par la détection des cycles. Il existe également du code standard pour construire la représentation de liste d'adjacence d'un graphique à partir d'une liste d'arêtes.
Lorsque ce script est exécuté, il imprime la sortie suivante:
Cycle detected: found a back edge from x to v.
Cycle detected: found a back edge from z to z.
Ce sont exactement les bords arrière de l'exemple de la figure 22.4 du CLRS.