Topologischer Sortieralgorithmus: Python, C++ Beispiel

โšก Intelligente Zusammenfassung

Die topologische Sortierung ordnet die Knoten eines gerichteten azyklischen Graphen so, dass jeder Knoten vor denjenigen erscheint, auf die er zeigt. Dabei wird Kahns Algorithmus verwendet, um wiederholt Knoten mit Eingangsgrad Null auszuwรคhlen.

  • ๐Ÿ“ Definition: Topological Sort erzeugt eine lineare Ordnung der DAG-Knoten, bei der jede gerichtete Kante (u, v) u vor v hat.
  • ๐Ÿ” Khans Algorithmus: Wรคhle wiederholt einen Knoten mit null eingehenden Kanten aus, fรผge ihn der Reihenfolge hinzu und verringere den Eingangsgrad seiner Nachbarn.
  • ๐Ÿšซ Zyklen blockiert: Ein Graph, der einen Zyklus enthรคlt, kann nicht topologisch sortiert werden, da innerhalb des Zyklus kein Knoten jemals den Eingangsgrad Null erreicht.
  • ๐Ÿ’ป Code: C++ und Python Die Implementierungen verwenden eine Warteschlange und ein Eingangsgrad-Array, um die Reihenfolge in O(V + E) Zeit zu berechnen.
  • ๐Ÿ“Š Komplexitรคt: Die Zeitkomplexitรคt betrรคgt O(V + E) und die Speicherkomplexitรคt O(V), wobei V die Anzahl der Knoten und E die Anzahl der Kanten ist.
  • ๏ธ Anwendungen: Aufgaben- und Buildplanung, Auflรถsung von Paketabhรคngigkeiten (apt, npm), Deadlock-Erkennung und Kursvoraussetzungen verwenden alle eine topologische Reihenfolge.

Topologischer Sortieralgorithmus

Was ist ein topologischer Sortieralgorithmus?

Die topologische Sortierung wird auch als Kahn-Algorithmus bezeichnet und ist ein beliebter Sortieralgorithmus. Unter Verwendung eines gerichteten Graphen als Eingabe sortiert Topological Sort die Knoten so, dass jeder vor dem Knoten erscheint, auf den er zeigt.

Dieser Algorithmus wird auf einen gerichteten azyklischen Graphen (DAG) angewendet, sodass jeder Knoten in der sortierten Liste vor allen anderen Knoten erscheint, auf die er verweist. Der Algorithmus befolgt bestimmte Regeln wiederholt, bis die Sortierung abgeschlossen ist.

Betrachten Sie zur Vereinfachung das folgende Beispiel:

Gerichteter Graph

Gerichteter Graph

Hier sehen wir, dass Knoten โ€žAโ€œ keinen Eingangsgrad hat. Der Eingangsgrad bezeichnet die Kante, die auf einen Knoten verweist. Knoten โ€žBโ€œ und โ€žCโ€œ benรถtigen Knoten โ€žAโ€œ, und Knoten โ€žEโ€œ benรถtigt Knoten โ€žDโ€œ und โ€žFโ€œ. Einige Knoten sind also voneinander abhรคngig.

Hier ist eine weitere Darstellung des obigen Graphen:

Abhรคngigkeit jedes Knotens

Abhรคngigkeit jedes Knotens (lineare Reihenfolge)

Wenn wir also den DAG (Directed Asymmetric Graph) an die topologische Sortierung รผbergeben, erhalten wir ein Array mit linearer Reihenfolge, bei dem das erste Element keine Abhรคngigkeit hat.

Topologischer Sortieralgorithmus

Hier sind die Schritte dazu:

Schritt 1) Suchen Sie den Knoten mit null eingehenden Kanten, einen Knoten mit null Grad.

Schritt 2) Speichere den Knoten mit dem Eingangsgrad Null in einer Warteschlange oder einem Stapel und entferne den Knoten aus dem Graphen.

Schritt 3) Lรถschen Sie anschlieรŸend die ausgehende Kante dieses Knotens. Dadurch wird der Eingangsgrad des nรคchsten Knotens verringert.

Die topologische Ordnung erfordert, dass die Graphdatenstruktur keine Zyklen enthรคlt. Ein Graph gilt als gerichteter azyklischer Graph (DAG), wenn er folgende Anforderungen erfรผllt:

  • Ein oder mehrere Knoten mit einem Gradwert von Null.
  • Der Graph enthรคlt keinen Zyklus.

Solange der Graph Knoten enthรคlt und ein gerichteter azyklischer Graph (DAG) ist, werden die oben genannten drei Schritte ausgefรผhrt. Andernfalls gerรคt der Algorithmus in eine zyklische Abhรคngigkeit, und der Kahn-Algorithmus findet keinen Knoten mit Eingangsgrad Null.

So funktioniert die topologische Sortierung

Hier verwenden wir den โ€žKahn-Algorithmusโ€œ fรผr die topologische Sortierung. Angenommen, wir haben den folgenden Graphen:

Topologische Sortierarbeiten

Hier sind die Schritte des Kahn-Algorithmus:

Schritt 1) Berechnen Sie den Eingangsgrad oder die Eingangskante aller Knoten im Diagramm.

Hinweis:

  • Ingrad bezeichnet die gerichteten Kanten, die auf den Knoten zeigen.
  • Unter Grad versteht man die gerichteten Kanten, die von einem Knoten ausgehen.

Hier sind der Eingangsgrad und der Ausgangsgrad des obigen Graphen:

Eingangsgrad und Ausgangsgrad

Schritt 2) Finde den Knoten mit dem Eingangsgrad Null bzw. mit null eingehenden Kanten. Ein Knoten mit dem Eingangsgrad Null hat keine Kanten, die zu diesem Knoten fรผhren. Knoten โ€žAโ€œ hat den Eingangsgrad Null, das heiรŸt, es gibt keine Kante, die auf Knoten โ€žAโ€œ zeigt. Daher fรผhren wir die folgenden Aktionen durch:

  • Entferne diesen Knoten und seine ausgehenden Kanten.
  • Platzieren Sie den Knoten zur Bestellung in der Warteschlange.
  • Aktualisiere den Eingangsgrad des Nachbarknotens von โ€žAโ€œ.

Topologische Sortierarbeiten

Schritt 3) Wir mรผssen einen Knoten mit Eingangsgrad null finden. In diesem Beispiel haben โ€žBโ€œ und โ€žCโ€œ einen Eingangsgrad von null. Wir kรถnnen einen der beiden auswรคhlen. Nehmen wir โ€žBโ€œ und entfernen ihn aus dem Graphen. AnschlieรŸend aktualisieren wir die Eingangsgrade der รผbrigen Knoten. Nach diesen Operationen sehen unser Graph und unsere Warteschlange wie folgt aus:

Topologische Sortierarbeiten

Schritt 4) Knoten โ€žCโ€œ hat keine eingehende Kante. Daher entfernen wir Knoten โ€žCโ€œ aus dem Graphen und fรผgen ihn der Warteschlange hinzu. Wir kรถnnen auch die von โ€žCโ€œ ausgehende Kante lรถschen. Unser Graph sieht nun folgendermaรŸen aus:

Topologische Sortierarbeiten

Schritt 5) Wir sehen, dass die Knoten โ€žDโ€œ und โ€žFโ€œ einen Eingangsgrad von null haben. Wir nehmen einen Knoten und fรผgen ihn der Warteschlange hinzu. Nehmen wir zuerst โ€žDโ€œ heraus. Dann betrรคgt der Eingangsgrad fรผr Knoten โ€žEโ€œ 1. Nun gibt es keine Verbindung mehr zwischen D und E. Dasselbe machen wir fรผr Knoten โ€žFโ€œ, und unser Ergebnis sieht dann wie folgt aus:

Topologische Sortierarbeiten

Schritt 6) Der Eingangsgrad (eingehende Kanten) und Ausgangsgrad (ausgehende Kanten) des Knotens โ€žEโ€œ sind null. Damit sind alle Voraussetzungen fรผr Knoten โ€žEโ€œ erfรผllt. Wir fรผgen โ€žEโ€œ nun am Ende der Warteschlange ein. Es sind keine Knoten mehr รผbrig, und der Algorithmus endet hier.

Topologische Sortierarbeiten

Spitzname Code fรผr topologische Sortierung

Hier ist der Pseudocode fรผr die topologische Sortierung unter Verwendung des Kahn-Algorithmus.

function TopologicalSort( Graph G ):
  for each node in G:
    calculate the indegree
  start = Node with 0 indegree
  G.remove(start)
  topological_list = [start]
  while node with 0 indegree present:
    topological_list.append(node)
    G.remove(node)
    // Update indegree of present nodes
  return topological_list

Die topologische Sortierung kann auch mit dem DFS implementiert werden (Tiefe Erste Suche) Methode. Dieser Ansatz ist jedoch die rekursive Methode. Kahns Algorithmus ist effizienter als der DFS-Ansatz.

C++ Implementierung der topologischen Sortierung

#include<bits/stdc++.h>
using namespace std;
class graph{
  int vertices;
  list<int> *adjecentList;
public:
  graph(int vertices){
    this->vertices = vertices;
    adjecentList = new list<int>[vertices];
  }
  void createEdge(int u, int v){
    adjecentList[u].push_back(v);
  }
  void TopologicalSort(){
    // filling the vector with zero initially
    vector<int> indegree_count(vertices,0);

    for(int i=0;i<vertices;i++){
      list<int>::iterator itr;
      for(itr=adjecentList[i].begin(); itr!=adjecentList[i].end();itr++){
        indegree_count[*itr]++;
      }
    }
    queue<int> Q;
    for(int i=0; i<vertices;i++){
      if(indegree_count[i]==0){
        Q.push(i);
      }
    }
    int visited_node = 0;
    vector<int> order;
    while(!Q.empty()){
      int u = Q.front();
      Q.pop();
      order.push_back(u);

      list<int>::iterator itr;
      for(itr=adjecentList[u].begin(); itr!=adjecentList[u].end();itr++){
        if(--indegree_count[*itr]==0){
          Q.push(*itr);
        }
      }
      visited_node++;
    }
    if(visited_node!=vertices){
      cout<<"There's a cycle present in the Graph.\nGiven graph is not DAG"<<endl;
      return;
    }
    for(int i=0; i<order.size();i++){
      cout<<order[i]<<"\t";
    }
  }
};
int main(){
  graph G(6);
  G.createEdge(0,1);
  G.createEdge(0,2);
  G.createEdge(1,3);
  G.createEdge(1,5);
  G.createEdge(2,3);
  G.createEdge(2,5);
  G.createEdge(3,4);
  G.createEdge(5,4);
  G.TopologicalSort();
}

Ausgang

0       1       2       3       5       4

Python Implementierung der topologischen Sortierung

from collections import defaultdict
class graph:
    def __init__(self, vertices):
        self.adjacencyList = defaultdict(list)
        self.Vertices = vertices  # No. of vertices
    # function to add an edge to adjacencyList
    def createEdge(self, u, v):
        self.adjacencyList[u].append(v)
    # The function to do Topological Sort.
    def topologicalSort(self):
        total_indegree = [0]*(self.Vertices)
        for i in self.adjacencyList:
            for j in self.adjacencyList[i]:
                total_indegree[j] += 1
        queue = []
        for i in range(self.Vertices):
            if total_indegree[i] == 0:
                queue.append(i)
        visited_node = 0
        order = []
        while queue:
            u = queue.pop(0)
            order.append(u)
            for i in self.adjacencyList[u]:
                total_indegree[i] -= 1

                if total_indegree[i] == 0:
                    queue.append(i)
            visited_node += 1
        if visited_node != self.Vertices:
            print("There's a cycle present in the Graph.\nGiven graph is not DAG")
        else:
            print(order)
G = graph(6)
G.createEdge(0,1)
G.createEdge(0,2)
G.createEdge(1,3)
G.createEdge(1,5)
G.createEdge(2,3)
G.createEdge(2,5)
G.createEdge(3,4)
G.createEdge(5,4)
G.topologicalSort()

Ausgang

[0, 1, 2, 3, 5, 4]

Zyklische Graphen des topologischen Sortieralgorithmus

Ein Graph, der einen Zyklus enthรคlt, kann nicht topologisch geordnet werden, da die Abhรคngigkeiten im zyklischen Graphen zyklisch sind. Betrachten Sie beispielsweise diesen Graphen:

Zyklische Graphen des topologischen Sortieralgorithmus

Dieser Graph ist kein DAG (gerichteter azyklischer Graph), da A, B und C einen Zyklus bilden. Es gibt keinen Knoten mit Eingangsgrad null. Analysiert man den obigen Graphen gemรครŸ Kahns Algorithmus, ergibt sich Folgendes:

  • Suchen Sie einen Knoten mit null Grad (keine eingehenden Kanten).
  • Entferne diesen Knoten aus dem Graphen und fรผge ihn der Warteschlange hinzu. Im obigen Graphen gibt es jedoch keinen Knoten mit Eingangsgrad Null. Jeder Knoten hat einen Eingangsgrad grรถรŸer als 0.
  • Es wird eine leere Warteschlange zurรผckgegeben, da kein Knoten mit Eingangsgrad Null gefunden werden konnte.

Wir kรถnnen Zyklen mithilfe der topologischen Ordnung mit den folgenden Schritten erkennen:

Schritt 1) Fรผhren Sie eine topologische Sortierung durch.

Schritt 2) Berechnen Sie die Gesamtzahl der Elemente in der topologisch sortierten Liste.

Schritt 3) Wenn die Anzahl der Elemente der Gesamtzahl der Knoten entspricht, dann gibt es keinen Zyklus.

Schritt 4) Wenn sie nicht gleich der Anzahl der Knoten ist, dann gibt es mindestens einen Zyklus in der gegebenen Graphdatenstruktur.

Komplexitรคtsanalyse der topologischen Sortierung

Es gibt zwei Arten von Komplexitรคt bei Algorithmen. Diese sind:

  1. Zeitliche Komplexitรคt
  2. Raumkomplexitรคt

Diese Komplexitรคten werden mit einer Funktion dargestellt, die eine allgemeine Komplexitรคt bereitstellt.

Zeitliche Komplexitรคt: Die Zeitkomplexitรคt des topologischen Sortierens ist immer gleich. Es gibt einen Worst-Case, einen Average und einen Best-Case. Die Zeitkomplexitรคt des topologischen Sortierens betrรคgt O(E + V), wobei E die Anzahl der Kanten und V die Anzahl der Knoten im Graphen bezeichnet.

Lassen Sie uns diese Komplexitรคt รผberwinden:

Schritt 1) Zu Beginn berechnen wir alle Ingrade. Dazu mรผssen wir alle Kanten durchgehen und zunรคchst allen V-Scheitelpunkten den Wert Null zuweisen. Die inkrementellen Schritte, die wir durchfรผhren, werden also sein O(V+E).

Schritt 2) Wir werden den Knoten mit dem Gradwert Null finden. Wir mรผssen anhand der V-Nummer des Scheitelpunkts suchen. Damit sind die Schritte abgeschlossen O (V).

Schritt 3) Fรผr jeden Knoten mit null Eingangsgraden entfernen wir diesen Knoten und verringern den Eingangsgrad. Die Durchfรผhrung dieser Operation fรผr alle Knoten dauert O(E).

Schritt 4) AbschlieรŸend prรผfen wir, ob es einen Zyklus gibt oder nicht. Wir prรผfen, ob die Gesamtzahl der Elemente im sortierten Array gleich der Gesamtzahl der Knoten ist. Es wird dauern O (1).

Dies waren also die einzelnen Zeitkomplexitรคten fรผr jeden Schritt der topologischen Sortierung bzw. topologischen Ordnung. Die Zeitkomplexitรคt ergibt sich aus der obigen Berechnung zu O(V + E), wobei O die Komplexitรคtsfunktion bezeichnet.

Raumkomplexitรคt: Fรผr die Ausfรผhrung des topologischen Sortieralgorithmus benรถtigten wir O(V) Speicherplatz. Hier sind die Schritte, fรผr die wir Speicherplatz im Programm benรถtigten:

  • Wir mussten alle Ingrade der im Diagramm vorhandenen Knoten berechnen. Da der Graph insgesamt V Knoten hat, mรผssen wir ein Array der GrรถรŸe V erstellen. Der benรถtigte Platz war also O (V).
  • Eine Queue-Datenstruktur wurde verwendet, um den Knoten mit einem Grad von Null zu speichern. Wir haben die Knoten mit dem Grad Null aus dem ursprรผnglichen Diagramm entfernt und sie in die Warteschlange gestellt. Dafรผr war der erforderliche Platz vorhanden O (V).
  • Das Array trรคgt den Namen โ€žorderโ€œ und speichert die Knoten in topologischer Reihenfolge. Das erforderte auch O (V) Rรคume.

Dies waren die individuellen Speicherkomplexitรคten. Wir mรผssen diese Speicherkapazitรคten also zur Laufzeit maximieren. Die Speicherkomplexitรคt wird mit O(V) bezeichnet, wobei V die Nummer des Knotens im Graphen angibt.

Anwendung der topologischen Sortierung

Topologische Sortierung hat vielfรคltige Einsatzmรถglichkeiten. Hier einige Beispiele:

  • Es wird verwendet, wenn ein Betriebssystem muss die Ressourcenzuweisung durchfรผhren.
  • Einen Zyklus im Graphen finden. Wir kรถnnen mithilfe der topologischen Sortierung รผberprรผfen, ob der Graph ein DAG ist oder nicht.
  • Satzreihenfolge in den Apps zur automatischen Vervollstรคndigung.
  • Es wird zur Erkennung verwendet. Deadlocks.
  • Verschiedene Arten der Stundenplanung oder Kursplanung nutzen die topologische Sortierung.
  • Abhรคngigkeiten auflรถsen. Wenn Sie beispielsweise versuchen, ein Paket zu installieren, benรถtigt dieses Paket mรถglicherweise auch andere Pakete. Durch die topologische Reihenfolge werden alle erforderlichen Pakete ermittelt, um das aktuelle Paket zu installieren.
  • Linux verwendet die topologische Sortierung in โ€žaptโ€œ, um die Abhรคngigkeit der Pakete zu รผberprรผfen.

Hรคufig gestellte Fragen

Die topologische Sortierung erzeugt eine lineare Ordnung der Knoten eines DAG, sodass bei jeder gerichteten Kante von u nach v u vor v in der Ordnung erscheint.

Jeder Zyklus fรคngt alle darin enthaltenen Knoten mit einem von Null verschiedenen Eingangsgrad ein, der niemals auf Null sinkt, sodass Kahns Algorithmus keinen nรคchsten Knoten auswรคhlen kann. Eine gรผltige topologische Ordnung erfordert einen gerichteten azyklischen Graphen.

Der Kahn-Algorithmus verwendet iterativ eine Warteschlange und Eingangsgradzรคhler. Der DFS-basierte topologische Sortieralgorithmus durchlรคuft den Graphen rekursiv und legt die fertigen Knoten auf einen Stapel. Beide haben eine Laufzeit von O(V + E).

Die Zeitkomplexitรคt betrรคgt O(V + E), da jeder Knoten und jede Kante genau einmal verarbeitet wird. Die Speicherkomplexitรคt betrรคgt O(V) fรผr das Eingangsgrad-Array, die Warteschlange und das Ausgabereihenfolge-Array.

Ja. Wenn zwei oder mehr Knoten im selben Schritt den Eingangsgrad Null haben, kann jeder von ihnen zuerst ausgewรคhlt werden. Unterschiedliche Auswahlreihenfolgen fรผhren zu unterschiedlichen gรผltigen topologischen Ordnungen desselben DAG.

Paketmanager wie apt, npm und pip verwenden die topologische Reihenfolge zur Auflรถsung von Abhรคngigkeiten. Auch Build-Systeme, Aufgabenplaner und Kursvoraussetzungsplaner basieren darauf.

Frameworks fรผr maschinelles Lernen wie TensorFlow und PyTorUm Vorwรคrts- und Rรผckwรคrtsdurchlรคufe zu planen, werden Berechnungsgraphen topologisch sortiert. Bayes'sche Netze benรถtigen ebenfalls eine topologische Ordnung der Variablen.

Ja. KI-gestรผtzte Copilot-Tools wie GitHub Copilot generieren Boilerplate-Code fรผr den Khan-Algorithmus. C++, Pythonden JavaDie Entwickler mรผssen noch die Zykluserkennung und die korrekte Warteschlangenverarbeitung รผberprรผfen.

Fassen Sie diesen Beitrag mit folgenden Worten zusammen: