Wie überprüfe ich, ob ein gerichteter Graph azyklisch ist? Und wie heißt der Algorithmus? Ich würde mich über eine Referenz freuen.
82
Wie überprüfe ich, ob ein gerichteter Graph azyklisch ist? Und wie heißt der Algorithmus? Ich würde mich über eine Referenz freuen.
Antworten:
Ich würde versuchen, das Diagramm topologisch zu sortieren , und wenn Sie nicht können, dann hat es Zyklen.
quelle
Eine einfache Tiefensuche ist nicht gut genug, um einen Zyklus zu finden. Es ist möglich, einen Knoten in einer DFS mehrmals zu besuchen, ohne dass ein Zyklus vorhanden ist. Je nachdem, wo Sie beginnen, besuchen Sie möglicherweise auch nicht das gesamte Diagramm.
Sie können in einer verbundenen Komponente eines Diagramms wie folgt nach Zyklen suchen. Suchen Sie einen Knoten, der nur ausgehende Kanten hat. Wenn es keinen solchen Knoten gibt, gibt es einen Zyklus. Starten Sie an diesem Knoten eine DFS. Überprüfen Sie beim Durchlaufen jeder Kante, ob die Kante auf einen Knoten zurückweist, der sich bereits auf Ihrem Stapel befindet. Dies zeigt die Existenz eines Zyklus an. Wenn Sie keine solche Kante finden, gibt es in dieser verbundenen Komponente keine Zyklen.
Wie Rutger Prins betont, müssen Sie die Suche für jede verbundene Komponente wiederholen, wenn Ihr Diagramm nicht verbunden ist.
Als Referenz ist Tarjans stark verbundener Komponentenalgorithmus eng verwandt. Es hilft Ihnen auch dabei, die Zyklen zu finden und nicht nur zu melden, ob sie vorhanden sind.
quelle
In Lemma 22.11 über das Buch
Introduction to Algorithms
(2. Auflage) heißt es:quelle
Lösung 1 : Kahn-Algorithmus zur Überprüfung des Zyklus . Hauptidee: Pflegen Sie eine Warteschlange, in der Knoten mit einem Grad von Null in die Warteschlange aufgenommen werden. Ziehen Sie dann den Knoten nacheinander ab, bis die Warteschlange leer ist. Überprüfen Sie, ob die In-Kanten eines Knotens vorhanden sind.
Lösung 2 : Tarjan-Algorithmus zur Überprüfung der stark verbundenen Komponente.
Lösung 3 : DFS . Verwenden Sie ein Integer-Array, um den aktuellen Status des Knotens zu kennzeichnen: dh 0 - bedeutet, dass dieser Knoten zuvor noch nicht besucht wurde. -1 - bedeutet, dass dieser Knoten besucht wurde und seine untergeordneten Knoten besucht werden. 1 - bedeutet, dass dieser Knoten besucht wurde und fertig ist. Wenn der Status eines Knotens während der DFS -1 ist, bedeutet dies, dass ein Zyklus vorhanden sein muss.
quelle
Die von ShuggyCoUk angegebene Lösung ist unvollständig, da möglicherweise nicht alle Knoten überprüft werden.
Dies hat die Zeitkomplexität O (n + m) oder O (n ^ 2)
quelle
m = O(n^2)
da das komplette Diagramm genaum=n^2
Kanten hat. Das ist es alsoO(n+m) = O(n + n^2) = O(n^2)
.Ich weiß, dass dies ein altes Thema ist, aber für zukünftige Suchende ist hier eine C # -Implementierung, die ich erstellt habe (kein Anspruch darauf, dass es am effizientesten ist!). Hierbei wird eine einfache Ganzzahl verwendet, um jeden Knoten zu identifizieren. Sie können das nach Belieben dekorieren, vorausgesetzt, Ihr Knotenobjekt hascht und ist gleich.
Bei sehr tiefen Graphen kann dies einen hohen Overhead verursachen, da an jedem Knoten in der Tiefe ein Hashset erstellt wird (diese werden über die Breite zerstört).
Sie geben den Knoten ein, von dem aus Sie suchen möchten, und geben den Pfad zu diesem Knoten an.
Wenn Sie nach Zyklen unterhalb eines bestimmten Knotens suchen, übergeben Sie diesen Knoten einfach zusammen mit einem leeren Hashset
quelle
Während der DFS sollte keine Hinterkante vorhanden sein. Während der DFS verfolgen Sie bereits besuchte Knoten. Wenn Sie auf eine Kante zwischen dem aktuellen Knoten und dem vorhandenen Knoten stoßen, hat der Graph einen Zyklus.
quelle
Hier ist ein schneller Code, um festzustellen, ob ein Graph Zyklen hat:
Die Idee ist wie folgt: Ein normaler dfs-Algorithmus mit einem Array zur Verfolgung der besuchten Knoten und ein zusätzliches Array, das als Markierung für die Knoten dient, die zum aktuellen Knoten geführt haben, sodass wir jedes Mal ein dfs für einen Knoten ausführen Wir setzen das entsprechende Element im Marker-Array auf true, sodass wir bei jedem Auftreten eines bereits besuchten Knotens prüfen, ob das entsprechende Element im Marker-Array true ist. Wenn es true ist, ist es einer der Knoten, die sich selbst zulassen (daher a Zyklus), und der Trick ist, wenn ein dfs eines Knotens zurückkehrt, setzen wir seinen entsprechenden Marker zurück auf false, damit wir uns nicht täuschen lassen, wenn wir ihn erneut von einer anderen Route aus besuchen.
quelle
Hier ist meine Ruby-Implementierung des Peel-Off-Leaf-Node-Algorithmus .
quelle
Hatte gerade diese Frage in einem Google-Interview.
Topologische Sortierung
Sie können versuchen, topologisch zu sortieren. Dies ist O (V + E), wobei V die Anzahl der Eckpunkte und E die Anzahl der Kanten ist. Ein gerichteter Graph ist genau dann azyklisch, wenn dies möglich ist.
Rekursive Blattentfernung
Die entfernen rekursiv Blattknoten, bis keine mehr übrig sind. Wenn mehr als ein Knoten übrig ist, haben Sie einen Zyklus. Wenn ich mich nicht irre, ist dies O (V ^ 2 + VE).
DFS-Stil ~ O (n + m)
Ein effizienter DFS-ähnlicher Algorithmus, Worst-Case O (V + E), ist jedoch:
quelle
Sie können die Umkehrung des Suchzyklus aus meiner Antwort hier https://stackoverflow.com/a/60196714/1763149 verwenden
quelle