Topologisk sorteringsalgoritme: Python, C++ Eksempel

โšก Smart opsummering

Topologisk sortering ordner noderne i en rettet acyklisk graf, sรฅ hver node vises fรธr dem, den peger pรฅ, ved hjรฆlp af Kahns algoritme til gentagne gange at vรฆlge noder med nul ingrad.

  • ๐Ÿ“ Definition: Topologisk sortering producerer en lineรฆr rรฆkkefรธlge af DAG-hjรธrner, hvor hver rettede kant (u, v) har u fรธr v.
  • ๐Ÿ” Kahns algoritme: Vรฆlg gentagne gange en node med nul indgรฅende kanter, tilfรธj den til rรฆkkefรธlgen, og dekrementer ingraden af โ€‹โ€‹dens naboer.
  • ๐Ÿšซ Blokerede cyklusser: En graf, der indeholder en cyklus, kan ikke topologisk sorteres, da ingen node nogensinde nรฅr nul i grader inde i cyklussen.
  • ๐Ÿ’ป Code: C++ og Python Implementeringer bruger en kรธ plus et indegree-array til at beregne rรฆkkefรธlgen i O(V + E) tid.
  • ๐Ÿ“Š kompleksitet: Tidskompleksiteten er O(V + E), og rumkompleksiteten er O(V), hvor V er antallet af hjรธrner og E er antallet af kanter.
  • ๐Ÿ› ๏ธ Applikationer: Opgave- og buildplanlรฆgning, lรธsning af pakkeafhรฆngigheder (apt, npm), deadlock-detektion og kursusforudsรฆtninger bruger alle topologisk rรฆkkefรธlge.

Topologisk sorteringsalgoritme

Hvad er topologisk sorteringsalgoritme?

Topologisk sortering er ogsรฅ kendt som Kahns algoritme og er en populรฆr sorteringsalgoritme. Ved at bruge en rettet graf som input sorterer Topologisk sortering noderne, sรฅ hver af dem vises fรธr den, den peger pรฅ.

Denne algoritme anvendes pรฅ en DAG (Directed Acyclic Graph), sรฅ hver node vises i det ordnede array fรธr alle andre noder, den peger pรฅ. Denne algoritme fรธlger visse regler gentagne gange, indtil sorteringen er fuldfรธrt.

For at forenkle, se pรฅ fรธlgende eksempel:

Instrueret graf

Instrueret graf

Her kan vi se, at "A" ikke har nogen ingrad. Ingrad betyder den kant, der peger pรฅ en node. "B" og "C" har en forudsรฆtning for "A", hvorefter "E" har en forudsรฆtning for "D"- og "F"-noder. Nogle af noderne er afhรฆngige af andre noder.

Her er en anden reprรฆsentation af ovenstรฅende graf:

Afhรฆngighed af hver Node

Afhรฆngighed af hver node (lineรฆr rรฆkkefรธlge)

Sรฅ nรฅr vi sender DAG (Directed Acyclic Graph) til den topologiske sortering, vil det give os en matrix med lineรฆr rรฆkkefรธlge, hvor det fรธrste element ikke har nogen afhรฆngighed.

Topologisk sorteringsalgoritme

Her er trinnene til at gรธre dette:

Trin 1) Find noden med nul indgรฅende kanter, en node med nul grader.

Trin 2) Gem den nul-i-graders-node i en kรธ eller stak, og fjern noden fra grafen.

Trin 3) Slet derefter den udgรฅende kant fra den node. Dette vil mindske antallet af grader for den nรฆste node.

Topologisk rรฆkkefรธlge krรฆver, at grafens datastruktur ikke har nogen cyklus. En graf vil blive betragtet som en DAG, hvis den opfylder disse krav:

  • En eller flere noder med en indegree-vรฆrdi pรฅ nul.
  • Grafen indeholder ingen cyklus.

Sรฅ lรฆnge der er noder i grafen, og grafen stadig er en DAG, vil vi kรธre de ovenstรฅende tre trin. Ellers vil algoritmen falde ind i den cykliske afhรฆngighed, og Kahns algoritme vil ikke vรฆre i stand til at finde en node med nul in-grad.

Sรฅdan fungerer topologisk sortering

Her vil vi bruge "Kahns algoritme" til den topologiske sortering. Lad os sige, at vi har fรธlgende graf:

Topologiske sorteringsvรฆrker

Her er trinnene til Kahns algoritme:

Trin 1) Beregn indegreen eller indgรฅende kant af alle noder i grafen.

Bemรฆrk:

  • Indegree betyder de rettede kanter, der peger pรฅ noden.
  • Outdegree betyder de rettede kanter, der kommer fra en node.

Her er ingraden og outgraden af โ€‹โ€‹ovenstรฅende graf:

Indre grad og udre grad

Trin 2) Find knuden med nul ingrader eller nul indgรฅende kanter. Knuden med nul ingrader betyder, at der ikke er nogen kanter, der peger mod knuden. Knude "A" har nul ingrader, hvilket betyder, at der ikke er nogen kant, der peger mod knude "A". Sรฅ vi vil udfรธre fรธlgende handlinger:

  • Fjern denne node og dens udadgรฅende kanter (udgรฅende kanter).
  • Placer noden i kรธen for bestilling.
  • Opdater antallet af grader for nabonoden til "A".

Topologiske sorteringsvรฆrker

Trin 3) Vi skal finde en node med en indegradsvรฆrdi pรฅ nul. I dette eksempel har "B" og "C" nul indegrad. Her kan vi tage en af โ€‹โ€‹disse to. Lad os tage "B" og slette den fra grafen. Derefter opdatere indegradsvรฆrdierne for de andre noder. Efter at have udfรธrt disse operationer, vil vores graf og kรธ se sรฅledes ud:

Topologiske sorteringsvรฆrker

Trin 4) Knudepunktet โ€œCโ€ har ingen indgรฅende kant. Sรฅ vi fjerner knudepunktet โ€œCโ€ fra grafen og sรฆtter det i kรธen. Vi kan ogsรฅ slette den kant, der er udgรฅende fra โ€œCโ€. Nu vil vores graf se sรฅdan ud:

Topologiske sorteringsvรฆrker

Trin 5) Vi kan se, at noderne "D" og "F" har ingraden nul. Vi tager en node og sรฆtter den i kรธen. Lad os fรธrst fjerne "D". Sรฅ vil ingradantallet for node "E" vรฆre 1. Nu vil der ikke vรฆre nogen node fra D til E. Vi skal gรธre det samme for node "F", og vores resultat vil vรฆre som fรธlger:

Topologiske sorteringsvรฆrker

Trin 6) Indgraden (indgรฅende kanter) og udgraden (udgรฅende kanter) for knudepunktet "E" blev nul. Sรฅ vi har opfyldt alle forudsรฆtningerne for knudepunktet "E". Her sรฆtter vi "E" i slutningen af โ€‹โ€‹kรธen. Sรฅ vi har ingen knudepunkter tilbage, og algoritmen slutter her.

Topologiske sorteringsvรฆrker

Kaldenavn Code til topologisk sortering

Her er pseudokoden for den topologiske sortering, mens Kahns algoritme bruges.

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

Topologisk sortering kan ogsรฅ implementeres ved hjรฆlp af DFS (Dybde fรธrste sรธgning) metode. Den tilgang er dog den rekursive metode. Kahns algoritme er mere effektiv end DFS-tilgangen.

C++ Implementering af Topologisk Sortering

#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();
}

Produktion

0       1       2       3       5       4

Python Implementering af Topologisk Sortering

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()

Produktion

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

Cykliske grafer af topologisk sorteringsalgoritme

En graf, der indeholder en cyklus, kan ikke topologisk ordnes, da den cykliske graf har afhรฆngigheden pรฅ en cyklisk mรฅde. Se for eksempel denne graf:

Cykliske grafer af topologisk sorteringsalgoritme

Denne graf er ikke en DAG (Directed Acyclic Graph), fordi A, B og C skaber en cyklus. Hvis du bemรฆrker det, er der ingen node med nul vรฆrdi i grader. Ifรธlge Kahns algoritme, hvis vi analyserer ovenstรฅende graf:

  • Find en node med nul grader (ingen indgรฅende kanter).
  • Fjern den node fra grafen og skub den til kรธen. I ovenstรฅende graf er der dog ingen node med nul grader i in-grader. Hver node har en graders vรฆrdi stรธrre end 0.
  • Returner en tom kรธ, da den ikke kunne finde nogen node med nul grader i grader.

Vi kan detektere cyklusser ved hjรฆlp af den topologiske rรฆkkefรธlge med fรธlgende trin:

Trin 1) Udfรธr topologisk sortering.

Trin 2) Beregn det samlede antal elementer i den topologisk sorterede liste.

Trin 3) Hvis antallet af elementer er lig med det samlede antal hjรธrner, er der ingen cyklus.

Trin 4) Hvis det ikke er lig med antallet af hjรธrner, er der mindst รฉn cyklus i den givne grafdatastruktur.

Kompleksitetsanalyse af topologisk sort

Der er to typer kompleksitet i algoritmer. De er:

  1. Tidskompleksitet
  2. Rumkompleksitet

Disse kompleksiteter er reprรฆsenteret med en funktion, der giver en generel kompleksitet.

Tidskompleksitet: Al tidskompleksitet er den samme for topologisk sortering. Der er vรฆrst tรฆnkelige, gennemsnitlige og bedste tรฆnkelige scenarier for tidskompleksitet. Tidskompleksiteten for topologisk sortering er O(E + V), hvor E stรฅr for antallet af kanter i grafen, og V stรฅr for antallet af hjรธrner i grafen.

Lad os bryde igennem denne kompleksitet:

Trin 1) I begyndelsen vil vi beregne alle graderne. For at gรธre det skal vi gรฅ gennem alle kanterne, og til at begynde med vil vi tildele alle V vertex-grader til nul. Sรฅ de trinvise trin, vi gennemfรธrer, vil vรฆre O(V+E).

Trin 2) Vi finder noden med nul indegree vรฆrdi. Vi skal sรธge fra toppunktets V-nummer. Sรฅ de gennemfรธrte trin vil vรฆre O (V).

Trin 3) For hver knude med nul grader, vil vi fjerne denne node og formindske indegreen. Det vil tage at udfรธre denne operation for alle noderne O(E).

Trin 4) Til sidst vil vi kontrollere, om der er nogen cyklus eller ej. Vi vil kontrollere, om det samlede antal elementer i det sorterede array er lig med det samlede antal noder. Det vil tage O (1).

Sรฅ dette var de individuelle tidskompleksiteter for hvert trin i den topologiske sortering eller topologiske ordning. Vi kan sige, at tidskompleksiteten fra ovenstรฅende beregning vil vรฆre O(V + E); her betyder O kompleksitetsfunktionen.

Rumkompleksitet: Vi havde brug for O(V)-rum til at kรธre den topologiske sorteringsalgoritme. Her er de trin, hvor vi havde brug for pladsen til programmet:

  • Vi var nรธdt til at beregne alle de grader af noder, der er til stede i grafen. Da grafen har i alt V-noder, er vi nรธdt til at skabe en matrix af stรธrrelse V. Sรฅ den nรธdvendige plads var O (V).
  • En kรธdatastruktur blev brugt til at lagre noden med nul indegree. Vi fjernede noderne med nul indegree fra den originale graf og placerede dem i kรธen. Til dette var den nรธdvendige plads O (V).
  • Arrayet hedder "orden", som gemte noderne i topologisk rรฆkkefรธlge. Det krรฆvede ogsรฅ O (V) rum.

Dette var de individuelle rumkompleksiteter. Sรฅ vi er nรธdt til at maksimere disse rum i lรธbetid. Rumkompleksitet stรฅr for O(V), hvor V stรฅr for antallet af hjรธrner i grafen.

Anvendelse af topologisk sort

Der er en enorm anvendelse for topologisk sortering. Her er nogle af dem:

  • Det bruges nรฅr en Operating system skal udfรธre ressourceallokeringen.
  • Find en cyklus i grafen. Vi kan validere, om grafen er en DAG eller ej, med topologisk sortering.
  • Sรฆtningsrรฆkkefรธlge i autofuldfรธrelsesapps.
  • Det bruges til at detektere blokeringer.
  • Forskellige typer planlรฆgning eller kursusplanlรฆgning bruger topologisk sortering.
  • Lรธsning af afhรฆngigheder. For eksempel, hvis du prรธver at installere en pakke, kan den pakke muligvis ogsรฅ have brug for andre pakker. Topologisk bestilling finder ud af alle de nรธdvendige pakker for at installere den aktuelle pakke.
  • Linux bruger den topologiske sortering i "apt" til at kontrollere pakkernes afhรฆngighed.

Ofte Stillede Spรธrgsmรฅl

Topologisk sortering producerer en lineรฆr rรฆkkefรธlge af hjรธrnerne i en DAG, sรฅledes at for hver rettet kant fra u til v vises u fรธr v i rรฆkkefรธlgen.

Enhver cyklus fanger hver node i den med en ingrad forskellig fra nul, der aldrig falder til nul, sรฅ Kahns algoritme kan ikke vรฆlge en nรฆste node. En gyldig topologisk orden krรฆver en rettet acyklisk graf.

Kahns algoritme bruger en kรธ og indegred-tรฆllere iterativt. DFS-baseret topologisk sortering rekurserer gennem grafen og skubber fรฆrdige noder til en stak. Begge kรธrer i O(V + E).

Tidskompleksiteten er O(V + E), da hvert hjรธrne og hver kant behandles รฉn gang. Rumkompleksiteten er O(V) for det indegrede array, kรธen og outputordre-arrayet.

Ja. Nรฅr to eller flere noder har nul ingrad pรฅ samme trin, kan begge vรฆlges fรธrst. Forskellige plukordner producerer forskellige gyldige topologiske ordener af den samme DAG.

Pakkeadministratorer som apt, npm og pip bruger topologisk rรฆkkefรธlge til afhรฆngighedslรธsning. Byggesystemer, opgaveplanlรฆggere og kursusforudsรฆtningsplanlรฆggere er ogsรฅ afhรฆngige af det.

Maskinlรฆringsframeworks som TensorFlow og PyTorch topologisk sorterer beregningsgrafer for at planlรฆgge fremadrettede og bagudrettede passager. Bayesianske netvรฆrk krรฆver ogsรฅ en topologisk rรฆkkefรธlge over variabler.

Ja. AI Copilot-vรฆrktรธjer som f.eks. GitHub Copilot genererer standardtekst for Kahns algoritme i C++, Python eller JavaUdviklere skal stadig verificere cyklusdetektion og korrekt kรธhรฅndtering.

Opsummer dette indlรฆg med: