Gemäß Lemma 22.11 von Cormen et al, Einführung in Algorithmen (CLRS):
Ein gerichteter Graph G ist azyklisch, wenn und nur wenn eine Tiefensuche von G keine Rückkanten ergibt.
Dies wurde bereits in mehreren Antworten erwähnt; hier werde ich auch ein Codebeispiel auf der Grundlage von Kapitel 22 des CLRS bereitstellen. Das Beispieldiagramm ist unten abgebildet.
CLRS-Pseudocode für Deep-First-Suchläufe:
In dem Beispiel in CLRS-Abbildung 22.4 besteht der Graph aus zwei DFS-Bäumen: Einer besteht aus Knoten u , v , x y y und der andere der Knotenpunkte w y z . Jeder Baum enthält eine Rückkante: eine von x a v und eine weitere von z a z (eine Selbstschleife).
Die wichtigste Erkenntnis ist, dass eine hintere Kante dann auftritt, wenn in der DFS-VISIT
Funktion, während sie über die Nachbarn iteriert v
の u
wird ein Knoten gefunden, der die GRAY
Farbe.
Der folgende Python-Code ist eine Anpassung des Pseudocodes von CLRS mit einer if
Klausel zur Erkennung von Zyklen hinzugefügt:
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 die time
im Pseudocode von CLRS wird nicht erfasst, da wir nur an der Erkennung von Zyklen interessiert sind. Es gibt auch einige Standardcodes für die Erstellung der Adjazenzlisten-Darstellung eines Graphen aus einer Liste von Kanten.
Wenn dieses Skript ausgeführt wird, gibt es die folgende Ausgabe aus:
Cycle detected: found a back edge from x to v.
Cycle detected: found a back edge from z to z.
Dies sind genau die hinteren Kanten in dem Beispiel in CLRS-Abbildung 22.4.
21 Stimmen
Sie sagen, Sie wollen alle Zyklen erkennen, aber Ihr Anwendungsfall legt nahe, dass es ausreichen würde, zu erkennen, ob es irgendwelche Zyklen gibt.
38 Stimmen
Es wäre besser, alle Zyklen aufzuspüren, damit sie in einem Zug behoben werden können, als zu prüfen, zu beheben, zu prüfen, zu beheben usw.
2 Stimmen
Sie sollten die Arbeit "Finding all the elementary circuits of a directed graph" von Donald B. Johnson lesen. Es werden zwar nur elementare Schaltungen gefunden, aber das sollte für Ihren Fall ausreichen. Und hier ist meine Java-Implementierung dieses Algorithmus zur Verwendung bereit: github.com/1123/johnson
0 Stimmen
Führen Sie DFS mit einer zusätzlichen Änderung für den Algorithmus aus: Markieren Sie jeden Knoten, den Sie besucht haben. Wenn Sie einen Knoten besuchen, der bereits besucht ist, haben Sie einen Cicle. Wenn Sie sich von einem Pfad zurückziehen, entfernen Sie die Markierung der besuchten Knoten.
9 Stimmen
@HeshamYassin, wenn Sie einen Knoten besuchen, den Sie bereits besucht haben, bedeutet das nicht unbedingt, dass es eine Schleife gibt. Bitte lesen Sie meinen Kommentar cs.stackexchange.com/questions/9676/ .
0 Stimmen
Ihr erster Satz widerspricht Ihrem letzten Satz; bitte korrigieren Sie ihn. Wenn Sie wirklich erkennen wollen alle die Zyklen (erster Satz), die Größe der Ausgabe und die Laufzeit sind im schlimmsten Fall exponentiell zur Größe der Eingabe. Wenn Sie wirklich nur den Fehlerfall von cualquier Zyklus (letzter Satz) können Sie das in einer Zeit tun, die linear zur Größe der Eingabe ist. Ich würde Letzteres empfehlen.
0 Stimmen
@Pneauters Nein, es wäre nicht unbedingt besser, alle Zyklen zu erkennen. Betrachten Sie den Fall, dass es eine exponentielle Anzahl von Zyklen gibt.
0 Stimmen
Sie können meine einfache und effektive Umsetzung nutzen: stackoverflow.com/a/60196714/1763149