Topológiai rendezési algoritmus: Python, C++ Példa

⚡ Okos összefoglaló

A topológiai rendezés egy irányított aciklikus gráf csomópontjait úgy rendezi el, hogy minden csomópont a rá mutatott csomópontok előtt jelenjen meg, Kahn algoritmusát használva a nulla beosztású csomópontok ismételt kiválasztására.

  • 📐 Meghatározás: A topológiai rendezés a DAG csúcsok lineáris sorrendjét eredményezi, ahol minden irányított él (u, v) előtt u található.
  • 🔁 Kahn algoritmusa: Ismételten válasszunk ki egy olyan csomópontot, amelynek nulla bejövő éle van, fűzzük hozzá a rendezéshez, és csökkentsük a szomszédai be nem ívelt fokszámát.
  • ???? Blokkolt ciklusok: Egy ciklust tartalmazó gráf nem topológiailag rendezhető, mivel a cikluson belül egyetlen csomópont sem éri el a nulla fokszámot.
  • ???? Code: C++ és a Python A megvalósítások egy sort és egy fokszámonként változó tömböt használnak a sorrend kiszámításához O(V + E) időben.
  • 📊 Bonyolultság: Az időbonyolultság O(V + E), a térbonyolultság pedig O(V), ahol V a csúcsok száma, E pedig az élek száma.
  • 🇧🇷 Alkalmazások: A feladat- és buildütemezés, a csomagfüggőségek feloldása (apt, npm), a holtpont-észlelés és a kurzus előfeltételei mind topológiai sorrendet használnak.

Topológiai rendezési algoritmus

Mi az a topológiai rendezési algoritmus?

A topológiai rendezés Kahn-algoritmusként is ismert, és egy népszerű rendezési algoritmus. Bemenetként irányított gráfot használva a Topológiai rendezés úgy rendezi a csomópontokat, hogy mindegyik az előtt jelenjen meg, amelyre mutat.

Ezt az algoritmust egy DAG-on (irányított aciklikus gráf) alkalmazzák, így minden csomópont a rendezett tömbben az összes többi általa mutatott csomópont előtt jelenik meg. Az algoritmus bizonyos szabályokat ismételten követ, amíg a rendezés be nem fejeződik.

Az egyszerűsítés érdekében nézze meg a következő példát:

Irányított grafikon

Irányított grafikon

Itt láthatjuk, hogy „A”-nak nincs független fokszáma. A független fok azt az élet jelenti, amely egy csomópontra mutat. „B” és „C” előfeltétele az „A”, majd „E” előfeltétele a „D” és „F” csomópontok. Néhány csomópont más csomópontoktól függ.

Íme a fenti grafikon egy másik ábrázolása:

Az egyes csomópontok függősége

Az egyes csomópontok függősége (lineáris rendezés)

Tehát amikor a DAG-ot (Directed Acyclic Graph) átadjuk a topológiai rendezésnek, akkor egy lineáris sorrendű tömböt adunk, ahol az első elemnek nincs függősége.

Topológiai rendezési algoritmus

Íme a lépések ehhez:

Step 1) Keresse meg a nulla bejövő élekkel rendelkező csomópontot, egy nulla fokos csomópontot.

Step 2) Tárold el a nulla fokszámú csomópontot egy várakozási sorban vagy veremben, majd távolítsd el a csomópontot a gráfból.

Step 3) Ezután törölje a kimenő élet az adott csomópontból. Ez csökkenti a következő csomópont bejövő fokszámát.

A topológiai sorrend megköveteli, hogy a gráf adatszerkezete ne tartalmazzon ciklust. Egy gráfot DAG-nak tekintünk, ha megfelel a következő követelményeknek:

  • Egy vagy több csomópont nulla infok értékkel.
  • A grafikon nem tartalmaz ciklust.

Amíg vannak csomópontok a gráfban, és a gráf továbbra is DAG, addig lefuttatjuk a fenti három lépést. Ellenkező esetben az algoritmus ciklikus függőségbe esik, és Kahn algoritmusa nem lesz képes nulla befokú csomópontot találni.

Hogyan működik a topológiai rendezés

Itt a „Kahn-algoritmust” fogjuk használni a topológiai rendezéshez. Tegyük fel, hogy a következő gráfunk van:

Topológiai rendezési munkák

Íme a Kahn-algoritmus lépései:

Step 1) Számítsa ki a Graph összes csomópontjának indokát vagy bejövő élét.

Jegyzet:

  • Az indegre a csomópontra mutató irányított éleket jelenti.
  • A külső fok az irányított éleket jelenti, amelyek egy csomópontból származnak.

Itt látható a fenti grafikon belső és külső fokszáma:

Fokozaton belüli és kívüli

Step 2) Keresd meg a nulla bejövő fokos vagy nulla bejövő éllel rendelkező csomópontot. A nulla bejövő fokos csomópont azt jelenti, hogy nem érkeznek élek a csomópont felé. Az „A” csomópont nulla bejövő fokos, ami azt jelenti, hogy nincs olyan él, amely az „A” csomópontra mutatna. Tehát a következő műveleteket fogjuk végrehajtani:

  • Távolítsd el ezt a csomópontot és a kifelé haladó éleit (kimenő éleit).
  • Helyezze a csomópontot a rendelési sorba.
  • Frissítse az „A” szomszédos csomópontjának fokszám szerinti számát.

Topológiai rendezési munkák

Step 3) Olyan csomópontot kell találnunk, amelynek a bejövő fok értéke nulla. Ebben a példában a „B” és a „C” bejövő foka nulla. Itt bármelyiket választhatjuk. Vegyük „B”-t, és töröljük a gráfból. Ezután frissítsük a többi csomópont bejövő fokának értékeit. Ezen műveletek végrehajtása után a gráfunk és a sorunk a következőképpen fog kinézni:

Topológiai rendezési munkák

Step 4) A „C” csomópontnak nincs bejövő éle. Tehát eltávolítjuk a „C” csomópontot a gráfból, és beillesztjük a sorba. Törölhetjük a „C” csomópontból kimenő élet is. A gráfunk most így fog kinézni:

Topológiai rendezési munkák

Step 5) Láthatjuk, hogy a „D” és „F” csomópontok fokszáma nulla. Fogunk egy csomópontot, és betesszük a sorba. Először vegyük ki a „D” csomópontot. Ekkor az „E” csomópont fokszáma 1 lesz. Most nem lesz D és E között csomópont. Ugyanezt kell tennünk az „F” csomóponttal is, és az eredmény a következő lesz:

Topológiai rendezési munkák

Step 6) Az „E” csomópont belső foka (bejövő élek) és külső foka (kimenő élek) nullává vált. Tehát teljesítettük az „E” csomópont összes előfeltételét. Itt az „E” csomópontot a sor végére tesszük. Tehát nem maradtak csomópontok, és az algoritmus itt véget ér.

Topológiai rendezési munkák

Pszeudo Code topológiai rendezésre

Itt a topológiai rendezés pszeudokódja Kahn algoritmusának használatával.

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

A topológiai rendezés is megvalósítható a DFS (Mélység első keresés) módszerrel. Ez a megközelítés azonban a rekurzív módszer. Kahn algoritmusa hatékonyabb, mint a DFS megközelítés.

C++ Topológiai rendezés megvalósítása

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

teljesítmény

0       1       2       3       5       4

Python Topológiai rendezés megvalósítása

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

teljesítmény

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

Topológiai rendezési algoritmus ciklikus grafikonjai

Egy ciklust tartalmazó gráf nem rendezhető topológiailag, mivel a ciklikus gráf ciklikus módon rendelkezik a függőséggel. Nézzük meg például ezt a gráfot:

Topológiai rendezési algoritmus ciklikus grafikonjai

Ez a gráf nem irányított aciklikus gráf (DAG), mivel A, B és C egy ciklust alkotnak. Ha megfigyeled, nincs olyan csomópont, amelynek a fokszáma nulla lenne. Kahn algoritmusa szerint, ha elemezzük a fenti gráfot:

  • Keressen egy nulla fokos csomópontot (nincs bejövő él).
  • Távolítsd el ezt a csomópontot a gráfból, és helyezd át a várólistába. A fenti gráfban azonban nincs olyan csomópont, amelynek a bejövő foka nulla. Minden csomópont bejövő fok értéke nagyobb, mint 0.
  • Üres várólistát ad vissza, mivel nem talált nulla in-fok értékű csomópontot.

A ciklusokat topológiai sorrendben tudjuk észlelni, a következő lépésekkel:

Step 1) Végezze el a topológiai rendezést.

Step 2) Számítsa ki a topológiailag rendezett lista elemeinek teljes számát.

Step 3) Ha az elemek száma megegyezik a csúcsok teljes számával, akkor nincs ciklus.

Step 4) Ha nem egyenlő a csúcsok számával, akkor legalább egy ciklus van az adott gráf adatstruktúrában.

Topológiai rendezés komplexitáselemzése

Az algoritmusokban kétféle bonyolultság létezik. Ezek a következők:

  1. Idő komplexitás
  2. Tér komplexitás

Ezeket a komplexitásokat egy általános komplexitást biztosító függvény ábrázolja.

Idő összetettsége: A topológiai rendezésnél minden időbonyolultság azonos. Az időbonyolultságnak létezik legrosszabb, átlagos és legjobb esete. A topológiai rendezés időbonyolultsága O(E + V), ahol E a gráf éleinek számát, V pedig a gráf csúcsainak számát jelenti.

Törjük át magunkat ezen a bonyolultságon:

Step 1) Kezdetben kiszámoljuk az összes fokot. Ehhez át kell mennünk az összes élen, és kezdetben az összes V csúcsindefot nullához rendeljük. Tehát az általunk végrehajtott fokozatos lépések a következők lesznek O(V+E).

Step 2) Megtaláljuk a nulla indegree értékű csomópontot. A csúcs V számából kell keresnünk. Tehát a lépések befejeződnek O(V).

Step 3) Minden nulla fokos csomópontnál eltávolítjuk azt a csomópontot, és csökkentjük az infokát. Ennek a műveletnek az összes csomóponton történő végrehajtása szükséges O(E).

Step 4) Végül ellenőrizzük, hogy van-e ciklus vagy nincs. Ellenőrizzük, hogy a rendezett tömb elemeinek teljes száma megegyezik-e a csomópontok teljes számával. El fog tartani O (1).

Tehát ezek voltak a topológiai rendezés vagy topológiai rendezés minden egyes lépésének egyedi időbonyolultságai. Azt mondhatjuk, hogy a fenti számításból származó időbonyolultság O(V + E) lesz; itt O a komplexitásfüggvényt jelenti.

Tér összetettsége: A topológiai rendezési algoritmus futtatásához O(V) szóközre volt szükségünk. Íme a lépések, amelyek során szükségünk volt a programhoz szükséges helyre:

  • Ki kellett számítanunk a grafikonon lévő csomópontok összes fokát. Mivel a grafikon összesen V csomópontot tartalmaz, létre kell hoznunk egy V méretű tömböt. Tehát a szükséges hely O(V).
  • Egy Queue adatszerkezetet használtunk a csomópont nulla fokozatú tárolására. Eltávolítottuk a nulla fokos csomópontokat az eredeti grafikonból, és a sorba helyeztük őket. Ehhez a szükséges hely volt O(V).
  • A tömb neve „order” (order), amely a csomópontokat topológiai sorrendben tárolja. Ehhez az is szükséges volt, hogy O(V) szóközök.

Ezek voltak az egyes térbonyolultságok. Tehát futásidőben maximalizálnunk kell ezeket a tereket. A térbonyolultság O(V)-vel jelölve van, ahol V a gráfban lévő csúcs számát jelenti.

Topológiai rendezés alkalmazása

A topológiai rendezésnek hatalmas felhasználási területei vannak. Íme néhány ezek közül:

  • Akkor használják, amikor egy Operadolog rendszer el kell végeznie az erőforrás-allokációt.
  • Ciklus keresése a gráfban. Topológiai rendezéssel ellenőrizhetjük, hogy a gráf DAG-e vagy sem.
  • Mondatsorrend az automatikus kiegészítõ alkalmazásokban.
  • Az észlelésre használják holtpontok.
  • A különböző ütemezési vagy kurzusütemezési típusok a topológiai rendezést használják.
  • Függőségek feloldása. Például, ha megpróbál telepíteni egy csomagot, annak más csomagokra is szüksége lehet. A topológiai rendezés megtalálja az összes szükséges csomagot az aktuális csomag telepítéséhez.
  • Linux a topológiai rendezést használja az „apt”-ban a csomagok függőségének ellenőrzésére.

GYIK

A topológiai rendezés a dinamikusan változó csoport (DIG) csúcsainak lineáris sorrendjét hozza létre úgy, hogy minden u-tól v-ig irányított él esetén az u a v előtt szerepeljen a rendezésben.

Bármely ciklus minden benne lévő csomópontot egy nullától eltérő, soha nullára nem csökkenő befokakkal csapdába ejt, így Kahn algoritmusa nem tud következő csomópontot választani. Az érvényes topológiai sorrendhez irányított aciklikus gráfra van szükség.

Kahn algoritmusa egy várakozási sort és fokszámonkénti számlálókat használ iteratívan. A DFS-alapú topológiai rendezés végigmegy a gráfon, és a kész csomópontokat egy verembe helyezi. Mindkettő O(V + E)-ben fut.

Az időbonyolultság O(V + E), mivel minden csúcs és él egyszer kerül feldolgozásra. A térbonyolultság O(V) a fokszámfüggetlen tömb, a sor és a kimeneti sorrend tömb esetében.

Igen. Amikor két vagy több csomópontnak nulla a bejövő fokszáma ugyanazon lépésben, bármelyiket ki lehet választani először. A különböző kivlasztási sorrendek ugyanazon DAG különböző érvényes topológiai sorrendjeit eredményezik.

Az olyan csomagkezelők, mint az apt, az npm és a pip, topológiai sorrendet használnak a függőségek feloldására. A buildrendszerek, a feladatütemezők és a kurzus előfeltétel-tervezők is erre támaszkodnak.

Gépi tanulási keretrendszerek, mint például a TensorFlow és a PyTorA ch topológiailag rendezi a számítási gráfokat az előre és hátra haladó menetek ütemezéséhez. A Bayes-hálózatok a változók feletti topológiai sorrendet is megkövetelik.

Igen. Az olyan AI Copilot eszközök, mint a GitHub Copilot, Kahn-algoritmus sablont generálnak... C++, Pythonvagy JavaA fejlesztőknek továbbra is ellenőrizniük kell a ciklusérzékelést és a helyes sorkezelést.

Foglald össze ezt a bejegyzést a következőképpen: