Nach Lemma 22.11 von Cormen et al., Einführung in Algorithmen (CLRS):
Ein gerichteter Graph G ist genau dann azyklisch, wenn eine Tiefensuche von G keine Hinterkanten ergibt.
Dies wurde in mehreren Antworten erwähnt; Hier werde ich auch ein Codebeispiel bereitstellen, das auf Kapitel 22 von CLRS basiert. Das Beispieldiagramm ist unten dargestellt.
Der Pseudocode von CLRS für die Tiefensuche lautet:
In dem Beispiel in CLRS Abbildung 22.4 besteht der Graph aus zwei DFS-Bäumen: einer besteht aus den Knoten u , v , x und y und der andere aus den Knoten w und z . Jeder Baum enthält eine Hinterkante: eine von x nach v und eine von z nach z (eine Selbstschleife).
Die Schlüsselrealisierung besteht darin, dass eine Hinterkante angetroffen wird, wenn in der DFS-VISIT
Funktion beim Iterieren über die Nachbarn v
von u
ein Knoten mit der GRAY
Farbe angetroffen wird.
Der folgende Python-Code ist eine Anpassung des CLRS-Pseudocodes mit einer if
hinzugefügten Klausel, die Zyklen erkennt:
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)
Beachten Sie, dass in diesem Beispiel der time
Pseudocode in CLRS nicht erfasst wird, da wir nur an der Erkennung von Zyklen interessiert sind. Es gibt auch einen Boilerplate-Code zum Erstellen der Adjazenzlistendarstellung eines Diagramms aus einer Liste von Kanten.
Wenn dieses Skript ausgeführt wird, wird die folgende Ausgabe gedruckt:
Cycle detected: found a back edge from x to v.
Cycle detected: found a back edge from z to z.
Dies sind genau die Hinterkanten im Beispiel in CLRS Abbildung 22.4.