Topoloogilise sortimise algoritm: Python, C++ Näide

⚡ Nutikas kokkuvõte

Topoloogiline sortimine järjestab suunatud atsüklilise graafi sõlmed nii, et iga sõlm kuvatakse enne neid, millele see osutab, kasutades Kahni algoritmi, et korduvalt valida nullastmega sõlmi.

  • 📐 Määratlus: Topoloogiline sortimine annab DAG-tippude lineaarse järjestuse, kus igal suunatud serval (u, v) on enne v tippu u.
  • 🔁 Kahni algoritm: Valige korduvalt sõlm, millel pole sissetulevaid servi, lisage see järjekorda ja vähendage selle naabrite sissetulevat astet.
  • 🚫 Blokeeritud tsüklid: Tsüklit sisaldavat graafi ei saa topoloogiliselt sorteerida, kuna tsükli sees ükski sõlm ei saavuta kunagi nulli kraadi.
  • 💻 Code: C++ ja Python implementatsioonid kasutavad järjekorda ja mitteastmelist massiivi järjekorra arvutamiseks O(V + E) aja jooksul.
  • 📊 Keerukus: Ajaline keerukus on O(V + E) ja ruumiline keerukus on O(V), kus V on tippude arv ja E on servade arv.
  • 🛠️ Rakendused: Ülesannete ja järkude ajastamine, paketi sõltuvuste lahendamine (apt, npm), ummikseisu tuvastamine ja kursuse eeltingimused kasutavad kõik topoloogilist järjekorda.

Topoloogilise sortimise algoritm

Mis on topoloogilise sortimise algoritm?

Topoloogilist sortimist tuntakse ka Kahni algoritmina ja see on populaarne sortimisalgoritm. Kasutades sisendina suunatud graafikut, sorteerib Topoloogiline sortimine sõlmed nii, et igaüks ilmub enne seda, millele see osutab.

Seda algoritmi rakendatakse suunatud atsüklilise graafi (DAG) puhul nii, et iga sõlm kuvatakse järjestatud massiivis enne kõiki teisi sõlmi, millele see osutab. See algoritm järgib teatud reegleid korduvalt, kuni sortimine on lõppenud.

Lihtsustamiseks vaadake järgmist näidet:

Suunatud graafik

Suunatud graafik

Siin näeme, et „A”-l puudub sõltumatu aste. Sisemine aste tähendab serva, mis osutab sõlmele. „B” ja „C” eelduseks on „A” ning „E” eelduseks on „D” ja „F” sõlmed. Mõned sõlmed sõltuvad teistest sõlmedest.

Siin on ülaltoodud graafiku teine ​​esitus:

Iga sõlme sõltuvus

Iga sõlme sõltuvus (lineaarne järjestamine)

Seega, kui edastame DAG-i (Directed Acyclic Graph) topoloogilisele sortimisele, annab see meile lineaarse järjestusega massiivi, kus esimene element ei sõltu.

Topoloogilise sortimise algoritm

Selleks toimige järgmiselt.

Step 1) Leidke null sissetuleva servaga sõlm, null kraadiga sõlm.

Step 2) Salvesta see nullastmeline sõlm järjekorda või pinu ja eemalda sõlm graafikult.

Step 3) Seejärel kustuta sellest sõlmest väljuv serv. See vähendab järgmise sõlme sissetulevate kraadide arvu.

Topoloogiline järjestus eeldab, et graafi andmestruktuuril ei tohi olla tsüklit. Graafi loetakse DAG-iks, kui see vastab järgmistele nõuetele:

  • Üks või mitu sõlme, mille inkraadi väärtus on null.
  • Graafik ei sisalda ühtegi tsüklit.

Seni kuni graafis on sõlmi ja graaf on endiselt DAG, teostame ülaltoodud kolm sammu. Vastasel juhul langeb algoritm tsüklilisse sõltuvusse ja Kahni algoritm ei suuda leida nullastmega sõlme.

Kuidas topoloogiline sortimine töötab

Siin kasutame topoloogiliseks sortimiseks „Kahni algoritmi“. Oletame, et meil on järgmine graaf:

Topoloogilised sortimistööd

Siin on Kahni algoritmi sammud:

Step 1) Arvutage graafiku kõigi sõlmede mittekraadine või sissetulev serv.

Märge:

  • Indegree tähendab sõlmele suunatud suunatud servi.
  • Outdegree tähendab suunatud servi, mis tulevad sõlmest.

Siin on ülaltoodud graafiku sise- ja välisaste:

Indegree ja Outdegree

Step 2) Leia sõlm, mille sisendkraadide arv on null või sissetulevate servade arv null. Null sisendkraadiga sõlm tähendab, et selle sõlme suunas ei tule ühtegi serva. Sõlmel „A“ on null sisendkraadi, mis tähendab, et ükski serv ei osuta sõlmele „A“. Seega teeme järgmised toimingud:

  • Eemalda see sõlm ja selle välisservad (väljuvad servad).
  • Asetage sõlm tellimise järjekorda.
  • Uuenda „A” naabersõlme astmete arvu.

Topoloogilised sortimistööd

Step 3) Peame leidma sõlme, mille indegrade väärtus on null. Selles näites on "B" ja "C" indegrade null. Siin võime võtta ükskõik kumma neist kahest. Võtame "B" ja kustutame selle graafikult. Seejärel uuendame teiste sõlmede indegrade väärtusi. Pärast nende toimingute tegemist näevad meie graaf ja järjekord välja järgmised:

Topoloogilised sortimistööd

Step 4) Sõlmel „C“ puudub sisenev serv. Seega eemaldame sõlme „C“ graafikult ja lisame selle järjekorda. Samuti saame kustutada serva, mis väljub sõlmest „C“. Nüüd näeb meie graaf välja selline:

Topoloogilised sortimistööd

Step 5) Näeme, et sõlmede "D" ja "F" iseaste on null. Võtame ühe sõlme ja paneme selle järjekorda. Eemaldame kõigepealt sõlme "D". Siis on sõlme "E" iseastete arv 1. Nüüd ei ole sõlmest D sõlme E ühtegi sõlme. Peame sama tegema sõlme "F" jaoks ja tulemus on järgmine:

Topoloogilised sortimistööd

Step 6) Sõlme „E“ sisekraad (sissetulevad servad) ja väliskraad (väljuvad servad) muutusid nulliks. Seega oleme täitnud kõik sõlme „E“ eeldused. Siin paneme „E“ järjekorra lõppu. Seega pole meil ühtegi sõlme alles ja algoritm lõpeb siin.

Topoloogilised sortimistööd

Pseudo Code topoloogiliseks sortimiseks

Siin on Kahni algoritmi kasutava topoloogilise sortimise pseudokood.

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

Topoloogilist sortimist saab realiseerida ka DFS-i (Sügavus Esimene otsing) meetod. See lähenemisviis on aga rekursiivne meetod. Kahni algoritm on tõhusam kui DFS-i lähenemisviis.

C++ Topoloogilise sorteerimise rakendamine

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

Väljund

0       1       2       3       5       4

Python Topoloogilise sorteerimise rakendamine

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

Väljund

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

Topoloogilise sortimisalgoritmi tsüklilised graafikud

Tsüklit sisaldavat graafi ei saa topoloogiliselt järjestada, kuna tsüklilisel graafil on sõltuvus tsüklilisel viisil. Näiteks vaadake seda graafi:

Topoloogilise sortimisalgoritmi tsüklilised graafikud

See graaf ei ole DAG (suunatud atsükliline graaf), kuna A, B ja C moodustavad tsükli. Nagu näete, pole ühtegi sõlme, mille astme väärtus oleks null. Kahni algoritmi kohaselt, kui analüüsime ülaltoodud graafi:

  • Leidke null kraadiga sõlm (ilma sissetulevate servadeta).
  • Eemalda see sõlm graafikult ja lisa see järjekorda. Ülaltoodud graafikul pole aga ühtegi sõlme, mille sisendkraadide väärtus oleks null. Igal sõlmel on sisendkraadide väärtus suurem kui 0.
  • Tagasta tühi järjekord, kuna ei leitud ühtegi sõlme, mille sissetulevate kraadide arv oleks null.

Tsükleid saame tuvastada topoloogilise järjestuse abil järgmiste sammudega:

Step 1) Tehke topoloogiline sortimine.

Step 2) Arvutage topoloogiliselt sorteeritud loendi elementide koguarv.

Step 3) Kui elementide arv on võrdne tippude koguarvuga, siis tsüklit ei ole.

Step 4) Kui see ei ole võrdne tippude arvuga, siis on antud graafi andmestruktuuris vähemalt üks tsükkel.

Topoloogilise sortimise keerukuse analüüs

Algoritmides on kahte tüüpi keerukust. Need on:

  1. Aja keerukus
  2. Ruumi keerukus

Need keerukused on esitatud funktsiooniga, mis annab üldise keerukuse.

Aja keerukus: Topoloogilise sortimise puhul on ajaline keerukus sama. Ajalise keerukuse puhul on olemas halvim, keskmine ja parim stsenaarium. Topoloogilise sortimise ajaline keerukus on O(E + V), kus E tähistab graafi servade arvu ja V tähistab graafi tippude arvu.

Murrame selle keerukuse üle:

Step 1) Alguses arvutame kõik astmed. Selleks peame läbima kõik servad ja algselt määrame kõik V tipu indegreid nulliks. Niisiis, järkjärgulised sammud, mida me lõpetame, on O(V+E).

Step 2) Leiame null indegree väärtusega sõlme. Peame otsima tipu V numbri järgi. Niisiis, sammud on lõpetatud O(V).

Step 3) Iga nullkraadiga sõlme puhul eemaldame selle sõlme ja vähendame inkrementi. Selle toimingu sooritamine kõigi sõlmede jaoks võtab aega O(E).

Step 4) Lõpuks kontrollime, kas tsükkel on olemas või mitte. Kontrollime, kas sorteeritud massiivi elementide koguarv võrdub sõlmede koguarvuga. See võtab O (1).

Seega olid need topoloogilise sortimise või topoloogilise järjestamise iga sammu individuaalsed ajalised keerukused. Võime öelda, et ülaltoodud arvutuse ajaline keerukus on O(V + E); siin tähendab O keerukusfunktsiooni.

Ruumi keerukus: Topoloogilise sortimise algoritmi käivitamiseks vajasime O(V) tühikuid. Siin on sammud, kus vajasime programmi jaoks ruumi:

  • Pidime arvutama kõik graafikul olevate sõlmede astmed. Kuna graafikul on kokku V-sõlme, peame looma massiivi suurusega V. Seega oli vaja ruumi O(V).
  • Nullindraadiga sõlme salvestamiseks kasutati Queue andmestruktuuri. Eemaldasime algsest graafikust nullindraadiga sõlmed ja paigutasime need järjekorda. Selleks oli vaja ruumi O(V).
  • Massiivi nimi on „order“ ja see salvestas sõlmed topoloogilises järjekorras. See nõudis ka O(V) tühikud.

Need olid individuaalsed ruumi keerukused. Seega peame need ruumid jooksuaja jooksul maksimeerima. Ruumi keerukus tähistab O(V), kus V tähistab graafi tipu numbrit.

Topoloogilise sortimise rakendamine

Topoloogilisel sortimisel on tohutu kasutusala. Siin on mõned neist:

  • Seda kasutatakse siis, kui Operaasjade süsteem vajab ressursside eraldamist.
  • Tsükli leidmine graafis. Topoloogilise sortimise abil saame kontrollida, kas tegemist on DAG-graafiga või mitte.
  • Lausete järjestamine automaatse täitmise rakendustes.
  • Seda kasutatakse tuvastamiseks ummikseisud.
  • Erinevat tüüpi ajakava või kursuste ajakava koostamisel kasutatakse topoloogilist sortimist.
  • Sõltuvuste lahendamine. Näiteks kui proovite installida paketti, võib see pakett vajada ka muid pakette. Topoloogiline järjestamine selgitab välja kõik praeguse paketi installimiseks vajalikud paketid.
  • Linux kasutab pakettide sõltuvuse kontrollimiseks topoloogilist sortimist "apt".

KKK

Topoloogiline sortimine loob DAG-i tippude lineaarse järjestuse nii, et iga suunatud serva korral u-st v-ni esineb järjestuses u enne v-d.

Iga tsükkel püüab iga selles oleva sõlme lõksu nullist erineva sissepoole suunatud astmega, mis ei lange kunagi nulli, seega Kahni algoritm ei saa valida järgmist sõlme. Kehtiv topoloogiline järjestus nõuab suunatud atsüklilist graafi.

Kahni algoritm kasutab järjekorda ja astmetevahelisi loendureid iteratiivselt. DFS-põhine topoloogiline sortimine käib läbi graafiku rekursiivselt ja asetab valmis sõlmed pinu. Mõlemad töötavad O(V + E)-s.

Ajaline keerukus on O(V + E), kuna iga tippu ja serva töödeldakse üks kord. Ruumiline keerukus on O(V) nii mitteastmelise massiivi, järjekorra kui ka väljundjärjestuse massiivi puhul.

Jah. Kui kahel või enamal sõlmel on samal sammul null siseaste, saab ükskõik kumma neist esimesena valida. Erinevad valikujärjekorrad annavad sama DAG-i erinevad kehtivad topoloogilised järjestused.

Paketihaldurid nagu apt, npm ja pip kasutavad sõltuvuste lahendamiseks topoloogilist järjestust. Seda kasutavad ka ehitussüsteemid, ülesannete ajakava koostajad ja kursuste eeltingimuste planeerijad.

Masinõppe raamistikud nagu TensorFlow ja PyTorch sorteerib arvutusgraafikuid topoloogiliselt, et ajastada edasi-tagasi käike. Bayesi võrgud nõuavad samuti muutujate topoloogilist järjestust.

Jah. AI Copiloti tööriistad, näiteks GitHub Copilot, genereerivad Kahni algoritmi malli. C++, Pythonvõi JavaArendajad peavad endiselt kontrollima tsükli tuvastamist ja järjekorra korrektset käsitlemist.

Võta see postitus kokku järgmiselt: