Tri topologique : Python, C++ Exemple d'algorithme

Quโ€™est-ce que lโ€™algorithme de tri topologique ?

Le tri topologique est รฉgalement connu sous le nom d'algorithme de Kahn et est un algorithme de tri populaire. En utilisant un graphe orientรฉ comme entrรฉe, Topological Sort trie les nล“uds afin que chacun apparaisse avant celui vers lequel il pointe.

Cet algorithme est appliquรฉ sur un DAG (Directed Acyclic Graph) afin que chaque nล“ud apparaisse dans le tableau ordonnรฉ avant que tous les autres nล“uds ne pointent vers lui. Cet algorithme suit certaines rรจgles ร  plusieurs reprises jusqu'ร  ce que le tri soit terminรฉ.

Pour simplifier, regardez l'exemple suivant :

Graphique dirigรฉ
Graphique dirigรฉ

Ici, nous pouvons voir que ยซ A ยป nโ€™a pas de diplรดme. Cela signifie le bord qui pointe vers un nล“ud. ยซ B ยป et ยซ C ยป ont un prรฉ-requis de ยซ A ยป, puis ยซ E ยป a un prรฉ-requis de nล“uds ยซ D ยป et ยซ F ยป. Certains nล“uds dรฉpendent dโ€™autres nล“uds.

Voici une autre reprรฉsentation du graphique ci-dessus :

Dรฉpendance de chaque nล“ud

Dรฉpendance de chaque nล“ud (Ordre Linรฉaire)

Ainsi, lorsque nous passons le DAG (Directed Acyclic Graph) au tri topologique, cela nous donnera un tableau avec un ordre linรฉaire, oรน le premier รฉlรฉment n'a aucune dรฉpendance.

Algorithme de tri topologique

Cet exemple montre un graphique avec un cycle :

Voici les รฉtapes pour ce faire :

ร‰tape 1) Trouvez le nล“ud avec zรฉro arรชte entrante, un nล“ud avec zรฉro degrรฉ.

ร‰tape 2) Stockez qui remet ร  zรฉro le nล“ud en degrรฉ dans une file d'attente ou une pile et supprime le nล“ud du graphique.

ร‰tape 3) Supprimez ensuite le bord sortant de ce nล“ud.

Cela diminuera le nombre de degrรฉs pour le nล“ud suivant.

L'ordre topologique nรฉcessite que la structure des donnรฉes du graphique n'ait aucun cycle.

Un graphique sera considรฉrรฉ comme un DAG sโ€™il respecte ces exigences :

  • Un ou plusieurs nล“uds avec une valeur indegree de zรฉro.
  • Le graphique ne contient aucun cycle

Tant qu'il y a des nล“uds dans le graphique et que le graphique est toujours DAG, nous exรฉcuterons les trois รฉtapes ci-dessus. Sinon, l'algorithme tombera dans la dรฉpendance cyclique et l'algorithme de Kahn ne pourra pas trouver un nล“ud avec un degrรฉ nul.

Comment fonctionne le tri topologique

Ici, nous utiliserons ยซ l'algorithme de Kahn ยป pour le tri topologique. Disons que nous avons le graphique suivant :

Travaux de tri topologique

Voici les รฉtapes de l'algorithme de Kahn :

ร‰tape 1) Calculez le degrรฉ entrant ou le bord entrant de tous les nล“uds du graphique.

ร€ noter:

  • Indegree signifie les bords dirigรฉs pointant vers le nล“ud.
  • Outdegree dรฉsigne les arรชtes dirigรฉes provenant dโ€™un nล“ud.

Voici le degrรฉ d'entrรฉe et de sortie du graphique ci-dessus :

Travaux de tri topologique

ร‰tape 2) Trouvez le nล“ud avec zรฉro degrรฉ ou zรฉro arรชte entrante.

Le nล“ud avec un degrรฉ nul signifie qu'aucun bord ne se dirige vers ce nล“ud. Le nล“ud ยซ A ยป a zรฉro degrรฉ, ce qui signifie qu'il n'y a aucun bord pointant vers le nล“ud ยซ A ยป.

Nous allons donc effectuer les actions suivantes :

  • Supprimez ce nล“ud et ses bords extรฉrieurs (bords sortants)
  • Placez le nล“ud dans la file d'attente pour la commande.
  • Mettez ร  jour le nombre de degrรฉs du nล“ud voisin de ยซ A ยป.

Travaux de tri topologique

ร‰tape 3) Nous devons trouver un nล“ud avec une valeur indegree de zรฉro. Dans cet exemple, ยซ B ยป et ยซ C ยป ont un degrรฉ nul.

Ici, nous pouvons prendre lโ€™un ou lโ€™autre de ces deux. Prenons ยซ B ยป et supprimons-le du graphique.

Mettez ensuite ร  jour les valeurs indegree des autres nล“uds.

Aprรจs avoir effectuรฉ ces opรฉrations, notre graphique et notre file d'attente ressembleront ร  ce qui suit :

Travaux de tri topologique

ร‰tape 4) Le nล“ud ยซ C ยป n'a pas de bord entrant. Nous allons donc supprimer le nล“ud ยซ C ยป du graphique et le placer dans la file d'attente.

Nous pouvons รฉgalement supprimer le bord sortant de ยซ C ยป.

Maintenant, notre graphique ressemblera ร  ceci :

Travaux de tri topologique

ร‰tape 5) On peut voir que les nล“uds ยซ D ยป et ยซ F ยป ont le degrรฉ zรฉro. Nous allons prendre un nล“ud et le mettre dans la file d'attente.

Supprimons d'abord ยซ D ยป. Ensuite, le nombre de degrรฉs pour le nล“ud ยซ E ยป sera de 1. Dรฉsormais, il n'y aura plus de nล“ud de D ร  E.

Nous devons faire la mรชme chose pour le nล“ud ยซ F ยป, notre rรฉsultat sera le suivant :

Travaux de tri topologique

ร‰tape 6) Le degrรฉ entrant (bords entrants) et le degrรฉ sortant (bords sortants) du nล“ud ยซ E ยป sont devenus nuls. Nous avons donc rempli tous les prรฉ-requis pour le nล“ud ยซ E ยป.

Ici, nous mettons ยซ E ยป ร  la fin de la file dโ€™attente. Il ne nous reste donc plus aucun nล“ud, donc l'algorithme se termine ici.

Travaux de tri topologique

Pseudo-code pour le tri topologique

Voici le pseudo-code pour le tri topologique en utilisant l'algorithme de 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 O indegree present:
    topological_list.append(node)
    G.remove(node)
    Update Indegree of present nodes
  Return topological_list

Le tri topologique peut รฉgalement รชtre implรฉmentรฉ ร  l'aide du DFS (Premiรจre recherche en profondeur) mรฉthode. Cependant, cette approche est la mรฉthode rรฉcursive. L'algorithme de Kahn est plus efficace que l'approche DFS.

C++ Implรฉmentation du tri topologique

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

Sortie

0       1       2       3       5       4

Python Implรฉmentation du tri topologique

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

Sortie

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

Graphiques cycliques de l'algorithme de tri topologique

Un graphe contenant un cycle ne peut pas รชtre ordonnรฉ topologiquement. Comme le graphique cyclique a la dรฉpendance de maniรจre cyclique.

Par exemple, vรฉrifiez ce graphique :

Graphiques cycliques de l'algorithme de tri topologique

Ce graphique n'est pas DAG (Directed Acyclic Graph) car A, B et C crรฉent un cycle. Si vous remarquez, il nโ€™y a aucun nล“ud avec une valeur en degrรฉ nulle.

Selon l'algorithme de Kahn, si l'on analyse le graphique ci-dessus :

  • Trouvez un nล“ud avec zรฉro degrรฉ (pas de bords entrants).
  • Supprimez ce nล“ud du graphique et placez-le dans la file d'attente.
    Cependant, dans le graphique ci-dessus, il nโ€™y a aucun nล“ud avec zรฉro en degrรฉs. Chaque nล“ud a une valeur en degrรฉ supรฉrieure ร  0.
  • Renvoie une file d'attente vide, car elle n'a trouvรฉ aucun nล“ud avec zรฉro en degrรฉs.

Nous pouvons dรฉtecter les cycles en utilisant l'ordre topologique avec les รฉtapes suivantes :

ร‰tape 1) Effectuez un tri topologique.

ร‰tape 2) Calculez le nombre total dโ€™รฉlรฉments dans la liste triรฉe topologiquement.

ร‰tape 3) Si le nombre dโ€™รฉlรฉments est รฉgal au nombre total de sommets, alors il nโ€™y a pas de cycle.

ร‰tape 4) S'il n'est pas รฉgal au nombre de sommets, alors il y a au moins un cycle dans la structure de donnรฉes du graphique donnรฉe.

Analyse de complexitรฉ du tri topologique

Il existe deux types de complexitรฉ dans les algorithmes. Ils sont

  1. Complexitรฉ temporelle
  2. Complexitรฉ spatiale

Ces complexitรฉs sont reprรฉsentรฉes par une fonction qui fournit une complexitรฉ gรฉnรฉrale.

Complexitรฉ temporelle: Toute complexitรฉ temporelle est la mรชme pour le tri topologique. Il existe des scรฉnarios pires, moyens et meilleurs en matiรจre de complexitรฉ temporelle.

La complexitรฉ temporelle pour le tri topologique est O(E + V), ici, E signifie le nombre d'arรชtes dans le graphe et V signifie le nombre de sommets dans le graphe.

Brisons cette complexitรฉ :

ร‰tape 1) Au dรฉbut, nous calculerons tous les degrรฉs. Pour ce faire, nous devons parcourir toutes les arรชtes et, dans un premier temps, nous attribuerons ร  zรฉro tous les degrรฉs du sommet V. Ainsi, les รฉtapes progressives que nous accomplirons seront O(V+T).

ร‰tape 2) Nous trouverons le nล“ud avec une valeur de degrรฉ nulle. Nous devons rechercher ร  partir du numรฉro V du sommet. Ainsi, les รฉtapes complรฉtรฉes seront O (V).

ร‰tape 3) Pour chaque nล“ud avec zรฉro degrรฉ, nous supprimerons ce nล“ud et dรฉcrรฉmenterons le degrรฉ. Effectuer cette opรฉration pour tous les nล“uds prendra O(E).

ร‰tape 4) Enfin, nous vรฉrifierons s'il y a un cycle ou non. Nous vรฉrifierons si le nombre total d'รฉlรฉments dans le tableau triรฉ est รฉgal au nombre total de nล“uds. ร‡a prendra O (1).

Il s'agissait donc de la complexitรฉ temporelle individuelle pour chaque รฉtape du tri topologique ou de l'ordre topologique. Nous pouvons dire que la complexitรฉ temporelle du calcul ci-dessus sera O( V + E ) ; ici, O signifie la fonction de complexitรฉ.

Complexitรฉ de l'espace: Nous avions besoin d'espaces O(V) pour exรฉcuter l'algorithme de tri topologique.

Voici les รฉtapes pour lesquelles nous avions besoin d'espace pour le programme :

  • Nous avons dรป calculer tous les degrรฉs de nล“uds prรฉsents dans le graphique. Comme le graphique a un total de nล“uds V, nous devons crรฉer un tableau de taille V. Ainsi, l'espace requis รฉtait O (V).
  • Une structure de donnรฉes Queue a รฉtรฉ utilisรฉe pour stocker le nล“ud avec un degrรฉ nul. Nous avons supprimรฉ les nล“uds avec un degrรฉ nul du graphique d'origine et les avons placรฉs dans la file d'attente. Pour cela, l'espace requis รฉtait O (V).
  • Le tableau est nommรฉ ยซ ordre ยป. Cela a stockรฉ les nล“uds dans l'ordre topologique. Cela nรฉcessitait รฉgalement O (V) les espaces.

Il s'agissait de la complexitรฉ de l'espace individuel. Nous devons donc maximiser ces espaces au moment de lโ€™exรฉcution.

La complexitรฉ spatiale signifie O(V), oรน V dรฉsigne le numรฉro du sommet dans le graphique.

Application du tri topologique

Le tri topologique est trรจs utilisรฉ. En voici quelques uns:

  • Il est utilisรฉ lorsque Systรจme exploitation doit effectuer lโ€™allocation des ressources.
  • Trouver un cycle dans le graphique. Nous pouvons valider si le graphe est DAG ou non avec un tri topologique.
  • Ordre des phrases dans les applications de saisie semi-automatique.
  • Il est utilisรฉ pour dรฉtecter impasses.
  • Diffรฉrents types de planification ou de planification de cours utilisent le tri topologique.
  • Rรฉsoudre les dรฉpendances. Par exemple, si vous essayez d'installer un package, ce package peut รฉgalement nรฉcessiter d'autres packages. L'ordre topologique dรฉcouvre tous les packages nรฉcessaires pour installer le package actuel.
  • Linux utilise le tri topologique dans ยซ apt ยป pour vรฉrifier la dรฉpendance des packages.

Rรฉsumez cet article avec :