Algoritmul de sortare topologică: Python, C++ Exemplu

⚡ Rezumat inteligent

Sortarea topologică ordonează nodurile unui graf aciclic direcționat astfel încât fiecare nod să apară înaintea celor la care indică, folosind algoritmul lui Kahn pentru a alege în mod repetat noduri cu grad zero.

  • 📐 Definiție: Sortarea topologică produce o ordine liniară a vârfurilor DAG unde fiecare muchie direcționată (u, v) are u înainte de v.
  • 🔁 Algoritmul lui Kahn: Alegeți în mod repetat un nod cu zero muchii de intrare, adăugați-l la ordine și decrementați gradul interior al vecinilor săi.
  • 🚫 Cicluri blocate: Un graf care conține un ciclu nu poate fi sortat topologic, deoarece niciun nod nu atinge vreodată gradul zero în interiorul ciclului.
  • 💻 Code: C++ și Python Implementările utilizează o coadă plus o matrice cu grade proprii pentru a calcula ordinea în timp O(V + E).
  • 📊 Complexitate: Complexitatea temporală este O(V + E), iar complexitatea spațială este O(V), unde V este numărul de vârfuri, iar E este numărul de muchii.
  • 🛠️ Aplicații: Planificarea sarcinilor și a versiunilor de compilare, rezolvarea dependențelor de pachete (apt, npm), detectarea blocajelor și cerințele preliminare ale cursului utilizează toate ordinea topologică.

Algoritm de sortare topologică

Ce este algoritmul de sortare topologică?

Sortarea topologică este cunoscută și ca algoritmul lui Kahn și este un algoritm de sortare popular. Folosind un grafic direcționat ca intrare, Sortarea topologică sortează nodurile astfel încât fiecare să apară înaintea celui către care indică.

Acest algoritm este aplicat pe un DAG (Graf Aciclic Direcționat) astfel încât fiecare nod să apară în matricea ordonată înaintea tuturor celorlalte noduri la care indică. Acest algoritm urmează în mod repetat anumite reguli până când sortarea este finalizată.

Pentru a simplifica, uitați-vă la următorul exemplu:

Graficul Dirijat

Graficul Dirijat

Aici, putem vedea că „A” nu are grad intern. Gradul intern reprezintă muchia care indică un nod. „B” și „C” au o precondiție de „A”, apoi „E” are o precondiție de noduri „D” și „F”. Unele dintre noduri sunt dependente de alte noduri.

Iată o altă reprezentare a graficului de mai sus:

Dependența fiecărui Nod

Dependența fiecărui nod (ordonare liniară)

Deci, când trecem DAG (Graficul aciclic direcționat) la sortarea topologică, ne va oferi o matrice cu ordonare liniară, în care primul element nu are nicio dependență.

Algoritm de sortare topologică

Iată pașii pentru a face acest lucru:

Pas 1) Găsiți nodul cu zero margini de intrare, un nod cu zero grade.

Pas 2) Stocați acel nod cu gradul zero într-o coadă sau o stivă și eliminați nodul din grafic.

Pas 3) Apoi ștergeți muchia de ieșire din acel nod. Aceasta va reduce numărul de grade în interior pentru următorul nod.

Ordonarea topologică impune ca structura de date a grafului să nu aibă niciun ciclu. Un graf va fi considerat un DAG dacă îndeplinește aceste cerințe:

  • Unul sau mai multe noduri cu o valoare de grad zero.
  • Graficul nu conține niciun ciclu.

Atâta timp cât există noduri în Graf și Graful este încă un DAG, vom rula cei trei pași de mai sus. În caz contrar, algoritmul va cădea în dependența ciclică, iar algoritmul lui Kahn nu va putea găsi un nod cu grad zero.

Cum funcționează sortarea topologică

Aici, vom folosi „Algoritmul lui Kahn” pentru sortarea topologică. Să presupunem că avem următorul graf:

Lucrări de sortare topologică

Iată pașii pentru algoritmul lui Kahn:

Pas 1) Calculați gradul sau marginea de intrare a tuturor nodurilor din grafic.

Notă:

  • Indegree înseamnă marginile direcționate îndreptate către nod.
  • Outdegree înseamnă marginile direcționate care provin dintr-un nod.

Iată gradul interior și gradul exterior ale graficului de mai sus:

Indegree și Outdegree

Pas 2) Găsiți nodul cu zero grade de intrare sau zero muchii de intrare. Nodul cu zero grade de intrare înseamnă că nicio muchie nu se îndreaptă spre acel nod. Nodul „A” are zero grade de intrare, ceea ce înseamnă că nu există nicio muchie care să indice spre nodul „A”. Așadar, vom face următoarele acțiuni:

  • Eliminați acest nod și muchiile sale exterioare (muchiile de ieșire).
  • Plasați nodul în coadă pentru comandă.
  • Actualizează numărul de grade în interiorul nodului vecin al lui „A”.

Lucrări de sortare topologică

Pas 3) Trebuie să găsim un nod cu o valoare a gradului interior zero. În acest exemplu, „B” și „C” au gradul interior zero. Aici, putem lua oricare dintre aceste două. Să luăm „B” și să-l ștergem din Grafic. Apoi actualizăm valorile gradului interior ale celorlalte noduri. După efectuarea acestor operațiuni, Graficul și Coada noastră vor arăta astfel:

Lucrări de sortare topologică

Pas 4) Nodul „C” nu are o muchie de intrare. Așadar, vom elimina nodul „C” din Graf și îl vom introduce în coadă. De asemenea, putem șterge muchia de ieșire din „C”. Acum, Graficul nostru va arăta astfel:

Lucrări de sortare topologică

Pas 5) Putem vedea că nodurile „D” și „F” au gradul interior zero. Vom lua un nod și îl vom pune în coadă. Să eliminăm mai întâi „D”. Apoi, numărul de grade interior pentru nodul „E” va fi 1. Acum, nu va exista niciun nod de la D la E. Trebuie să facem același lucru pentru nodul „F”, iar rezultatul nostru va fi următorul:

Lucrări de sortare topologică

Pas 6) Gradul interior (muchiile de intrare) și gradul exterior (muchiile de ieșire) ale nodului „E” au devenit zero. Deci, am îndeplinit toate cerințele preliminare pentru nodul „E”. Aici, vom pune „E” la sfârșitul cozii. Deci, nu ne-au mai rămas noduri, iar algoritmul se termină aici.

Lucrări de sortare topologică

Pseudo Code pentru sortare topologică

Iată pseudo-codul pentru sortarea topologică folosind algoritmul lui Kahn.

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

Sortarea topologică poate fi implementată și folosind DFS (Căutare în adâncime) metoda. Cu toate acestea, această abordare este metoda recursivă. Algoritmul lui Kahn este mai eficient decât abordarea DFS.

C++ Implementarea sortării topologice

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

producție

0       1       2       3       5       4

Python Implementarea sortării topologice

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

producție

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

Grafice ciclice ale algoritmului de sortare topologică

Un graf care conține un ciclu nu poate fi ordonat topologic, deoarece graful ciclic are dependența într-o manieră ciclică. De exemplu, verificați acest graf:

Grafice ciclice ale algoritmului de sortare topologică

Acest graf nu este un DAG (graf aciclic direcționat) deoarece A, B și C creează un ciclu. Dacă observați, nu există niciun nod cu valoare zero în grad. Conform algoritmului lui Kahn, dacă analizăm graful de mai sus:

  • Găsiți un nod cu zero grade (fără margini de intrare).
  • Eliminați acel nod din Grafic și mutați-l în Coadă. Totuși, în Graficul de mai sus, nu există niciun nod cu zero grade în interior. Fiecare nod are o valoare a gradelor în interior mai mare decât 0.
  • Returnează o coadă goală, deoarece nu a putut găsi niciun nod cu zero grade în interior.

Putem detecta ciclurile folosind ordinea topologică cu următorii pași:

Pas 1) Efectuați sortarea topologică.

Pas 2) Calculați numărul total de elemente din lista sortată topologic.

Pas 3) Dacă numărul de elemente este egal cu numărul total de vârfuri, atunci nu există ciclu.

Pas 4) Dacă nu este egal cu numărul de vârfuri, atunci există cel puțin un ciclu în structura de date a grafului dată.

Analiza complexității sortării topologice

Există două tipuri de complexitate în algoritmi. Acestea sunt:

  1. Complexitatea timpului
  2. Complexitatea spațială

Aceste complexități sunt reprezentate cu o funcție care oferă o complexitate generală.

Complexitatea timpului: Complexitatea temporală este aceeași pentru sortarea topologică. Există scenarii negative, medii și pozitive pentru complexitatea temporală. Complexitatea temporală pentru sortarea topologică este O(E + V), unde E reprezintă numărul de muchii din graf, iar V reprezintă numărul de vârfuri din graf.

Să depășim această complexitate:

Pas 1) La început, vom calcula toate gradele. Pentru a face acest lucru, trebuie să trecem prin toate muchiile și, inițial, vom atribui toate V indexurile de vârf la zero. Deci, pașii incrementali pe care îi parcurgem vor fi O(V+E).

Pas 2) Vom găsi nodul cu valoare de grade zero. Trebuie să căutăm din numărul V al vârfului. Deci, pașii parcurși vor fi O(V).

Pas 3) Pentru fiecare nod cu zero grade, vom elimina acel nod și vom decrementa indicele. Efectuarea acestei operații pentru toate nodurile va dura O(E).

Pas 4) În cele din urmă, vom verifica dacă există sau nu vreun ciclu. Vom verifica dacă numărul total de elemente din tabloul sortat este egal cu numărul total de noduri. Va dura O (1).

Deci, acestea au fost complexitățile temporale individuale pentru fiecare pas al sortării topologice sau al ordonării topologice. Putem spune că complexitatea temporală din calculul de mai sus va fi O(V + E); aici, O reprezintă funcția de complexitate.

Complexitatea spațială: Aveam nevoie de spații O(V) pentru rularea algoritmului de sortare topologică. Iată pașii în care am avut nevoie de spațiul pentru program:

  • A trebuit să calculăm toate gradele nodurilor prezente în grafic. Deoarece Graficul are un total de V noduri, trebuie să creăm o matrice de dimensiunea V. Deci, spațiul necesar a fost O(V).
  • A fost folosită o structură de date coadă pentru a stoca nodul cu zero grade. Am eliminat nodurile cu zero indegree din graficul original și le-am plasat în coadă. Pentru aceasta, spațiul necesar a fost O(V).
  • Tabloul este denumit „order”, care a stocat nodurile în ordine topologică. Aceasta a necesitat, de asemenea, O(V) spații.

Acestea au fost complexitățile spațiale individuale. Așadar, trebuie să maximizăm aceste spații în timpul execuției. Complexitatea spațială reprezintă O(V), unde V reprezintă numărul vârfurilor din graf.

Aplicarea sortării topologice

Sortarea topologică are o utilizare enormă. Iată câteva dintre ele:

  • Se folosește atunci când un Operasistem de ting trebuie să efectueze alocarea resurselor.
  • Găsirea unui ciclu în graf. Putem valida dacă graful este un DAG sau nu cu sortare topologică.
  • Ordonarea propozițiilor în aplicațiile de completare automată.
  • Se folosește pentru detectarea blocaje.
  • Diferite tipuri de planificare sau planificare a cursurilor utilizează sortarea topologică.
  • Rezolvarea dependențelor. De exemplu, dacă încercați să instalați un pachet, acel pachet ar putea avea nevoie și de alte pachete. Ordonarea topologică descoperă toate pachetele necesare pentru a instala pachetul curent.
  • Linux folosește sortarea topologică în „apt” pentru a verifica dependența pachetelor.

Întrebări frecvente

Sortarea topologică produce o ordonare liniară a vârfurilor unui DAG astfel încât pentru fiecare muchie direcționată de la u la v, u apare înaintea lui v în ordonare.

Orice ciclu captează fiecare nod din el cu un grad intern diferit de zero, care nu scade niciodată la zero, deci algoritmul lui Kahn nu poate alege un nod următor. O ordine topologică validă necesită un graf aciclic direcționat.

Algoritmul lui Kahn folosește iterativ o coadă și contoare de grad intern. Sortarea topologică bazată pe DFS parcurge recurent graficul și împinge nodurile terminate într-o stivă. Ambele rulează în O(V + E).

Complexitatea temporală este O(V + E) deoarece fiecare vârf și muchie este procesată o singură dată. Complexitatea spațială este O(V) pentru tabloul cu grade interne, coada de așteptare și tabloul cu ordinea de ieșire.

Da. Când două sau mai multe noduri au gradul interior zero în același pas, oricare poate fi ales primul. Ordine diferite de alegere produc ordonări topologice valide diferite ale aceluiași DAG.

Managerii de pachete precum apt, npm și pip utilizează ordinea topologică pentru rezolvarea dependențelor. Sistemele de compilare, planificatoarele de sarcini și planificatoarele de cerințe preliminare ale cursului se bazează, de asemenea, pe aceasta.

Framework-uri de învățare automată precum TensorFlow și PyTorch sortează topologic graficele de calcul pentru a programa trecerile înainte și înapoi. Rețelele bayesiene necesită, de asemenea, o ordine topologică asupra variabilelor.

Da. Instrumentele AI Copilot, cum ar fi GitHub Copilot, generează scheme standard ale algoritmului lui Kahn în C++, Python, JavaDezvoltatorii trebuie în continuare să verifice detectarea ciclului și gestionarea corectă a cozii.

Rezumați această postare cu: