Einführung
In der Informatik ist ein gerichteter azyklischer Graph (DAG) ein gerichteter Graph ohne Zyklen. In diesem Tutorium zeigen wir, wie man eine topologische Sortierung auf einem DAG in linearer Zeit durchführt.
Topologische Sortierung
In vielen Anwendungen verwenden wir gerichtete azyklische Graphen, um Prioritäten zwischen Ereignissen anzugeben. Zum Beispiel gibt es bei einem Planungsproblem eine Menge von Aufgaben und eine Menge von Beschränkungen, die die Reihenfolge dieser Aufgaben angeben. Wir können ein DAG konstruieren, um Aufgaben darzustellen. Die gerichteten Kanten des DAG stellen die Reihenfolge der Aufgaben dar.
Betrachten wir ein Problem, bei dem es darum geht, wie sich eine Person für einen formellen Anlass anziehen könnte. Man muss mehrere Kleidungsstücke anziehen, z. B. Socken, Hosen, Schuhe usw. Einige Kleidungsstücke müssen vor den anderen angezogen werden. Zum Beispiel müssen wir die Socken vor den Schuhen anziehen. Einige Kleidungsstücke können in beliebiger Reihenfolge angezogen werden, z. B. Socken und Hosen. Wir können ein DAG verwenden, um dieses Problem zu veranschaulichen:

In diesem DAG entsprechen die Knoten den Kleidungsstücken. Eine direkte Kante  in der DAG zeigt an, dass wir das Kleidungsstück
 in der DAG zeigt an, dass wir das Kleidungsstück  vor dem Kleidungsstück
 vor dem Kleidungsstück  anziehen müssen. Unsere Aufgabe ist es, eine Reihenfolge für das Anlegen aller Kleidungsstücke zu finden und dabei die Abhängigkeitsbedingungen zu beachten. Zum Beispiel können wir die Kleidungsstücke in der folgenden Reihenfolge anziehen:
 anziehen müssen. Unsere Aufgabe ist es, eine Reihenfolge für das Anlegen aller Kleidungsstücke zu finden und dabei die Abhängigkeitsbedingungen zu beachten. Zum Beispiel können wir die Kleidungsstücke in der folgenden Reihenfolge anziehen:

Eine topologische Sortierung einer DAG  ist eine lineare Anordnung aller ihrer Eckpunkte, so dass, wenn
 ist eine lineare Anordnung aller ihrer Eckpunkte, so dass, wenn  eine Kante
 eine Kante  enthält,
 enthält,  in der Anordnung vor
 in der Anordnung vor  erscheint. Für ein DAG können wir eine topologische Sortierung konstruieren, deren Laufzeit linear zur Anzahl der Knoten plus der Anzahl der Kanten ist, also
 erscheint. Für ein DAG können wir eine topologische Sortierung konstruieren, deren Laufzeit linear zur Anzahl der Knoten plus der Anzahl der Kanten ist, also  .
.
Kahns Algorithmus
In einem DAG hat jeder Pfad zwischen zwei Knoten eine endliche Länge, da der Graph keinen Zyklus enthält. Sei  der längste Weg von
 der längste Weg von  (Quellknoten) nach
 (Quellknoten) nach  (Zielknoten). Da
 (Zielknoten). Da  der längste Pfad ist, kann es keine eingehende Kante zu
 der längste Pfad ist, kann es keine eingehende Kante zu  geben. Daher enthält eine DAG mindestens einen Knoten, der keine eingehende Kante hat.
 geben. Daher enthält eine DAG mindestens einen Knoten, der keine eingehende Kante hat.
3.1. Kahns Algorithmus
In Kahns Algorithmus konstruieren wir eine topologische Sortierung auf einer DAG, indem wir Knoten finden, die keine eingehenden Kanten haben:

In diesem Algorithmus berechnen wir zuerst die In-Grad-Werte für alle Knoten.
Dann beginnen wir mit einem Knoten, dessen In-Grad 0 ist, und setzen ihn an das Ende der Ausgabeliste. Sobald wir einen Scheitelpunkt ausgewählt haben, aktualisieren wir die In-Grad-Werte seiner benachbarten Scheitelpunkte, da der Scheitelpunkt und seine Außenkanten aus dem Graphen entfernt werden.
Wir können den obigen Vorgang wiederholen, bis wir alle Scheitelpunkte ausgewählt haben. Die Ausgabeliste ist dann eine topologische Sortierung des Graphen.
3.2. Zeitkomplexität
Um die in-degrees aller Scheitelpunkte zu berechnen, müssen wir alle Scheitelpunkte und Kanten von  besuchen. Daher beträgt die Laufzeit
 besuchen. Daher beträgt die Laufzeit  für In-degree-Berechnungen. Um zu vermeiden, dass diese Werte erneut berechnet werden müssen, können wir ein Array verwenden, um die In-Grad-Werte dieser Scheitelpunkte zu speichern. Innerhalb der while-Schleife müssen wir auch alle Knoten und Kanten besuchen. Daher beträgt die Gesamtlaufzeit
 für In-degree-Berechnungen. Um zu vermeiden, dass diese Werte erneut berechnet werden müssen, können wir ein Array verwenden, um die In-Grad-Werte dieser Scheitelpunkte zu speichern. Innerhalb der while-Schleife müssen wir auch alle Knoten und Kanten besuchen. Daher beträgt die Gesamtlaufzeit  .
.
Depth First Search
Wir können auch einen Depth First Search (DFS) Algorithmus verwenden, um die topologische Sortierung zu erstellen.
4.1. Graph DFS mit Rekursion
Bevor wir den Aspekt der topologischen Sortierung mit DFS in Angriff nehmen, wollen wir zunächst einen rekursiven Standard-DFS-Algorithmus zur Durchquerung von Graphen betrachten:

Im Standard-DFS-Algorithmus beginnen wir mit einem zufälligen Knoten in  und markieren diesen Knoten als besucht. Dann rufen wir rekursiv die Funktion dfsRecursive auf, um alle nicht besuchten benachbarten Scheitelpunkte zu besuchen. Auf diese Weise können wir alle Scheitelpunkte von
 und markieren diesen Knoten als besucht. Dann rufen wir rekursiv die Funktion dfsRecursive auf, um alle nicht besuchten benachbarten Scheitelpunkte zu besuchen. Auf diese Weise können wir alle Scheitelpunkte von  in
 in  Zeit besuchen.
 Zeit besuchen.
Da wir jedoch jeden Scheitelpunkt sofort in die Liste aufnehmen, wenn wir ihn besuchen, und da wir an jedem beliebigen Scheitelpunkt beginnen können, kann der Standard-DFS-Algorithmus nicht garantieren, dass er eine topologisch sortierte Liste erzeugt.
Lassen Sie uns sehen, was wir tun müssen, um diesen Mangel zu beheben.
4.2. DFS-basierter topologischer Sortieralgorithmus
Wir können den DFS-Algorithmus modifizieren, um eine topologische Sortierung einer DAG zu erzeugen. Während der DFS-Traversierung, nachdem alle Nachbarn eines Scheitelpunkts  besucht wurden, setzen wir ihn an den Anfang der Ergebnisliste
 besucht wurden, setzen wir ihn an den Anfang der Ergebnisliste  . Auf diese Weise können wir sicherstellen, dass
. Auf diese Weise können wir sicherstellen, dass  vor allen seinen Nachbarn in der sortierten Liste erscheint:
 vor allen seinen Nachbarn in der sortierten Liste erscheint:

Dieser Algorithmus ähnelt dem Standard-DFS-Algorithmus. Obwohl wir immer noch mit einem zufälligen Knoten beginnen, setzen wir den Knoten nicht sofort in die Liste, wenn wir ihn besuchen. Stattdessen werden zunächst alle seine Nachbarn rekursiv besucht und der Knoten dann an den Anfang der Liste gesetzt. Wenn also  eine Kante
 eine Kante  enthält, erscheint
 enthält, erscheint  vor
 vor  in der Liste.
 in der Liste.
Die Gesamtlaufzeit beträgt ebenfalls  , da sie die gleiche Zeitkomplexität wie der DFS-Algorithmus hat.
, da sie die gleiche Zeitkomplexität wie der DFS-Algorithmus hat.
Schlussfolgerung
In diesem Artikel haben wir ein Beispiel für topologisches Sortieren auf einem DAG gezeigt. Außerdem haben wir zwei Algorithmen diskutiert, die eine topologische Sortierung in  Zeit durchführen können.
 Zeit durchführen können.
Die Java-Implementierung des DFS-basierten topologischen Sortieralgorithmus ist in unserem Artikel über Depth First Search in Java verfügbar.