logo

Tri topologique

Tri topologique pour Graphique acyclique dirigé (DAG) est un ordre linéaire des sommets tel que pour chaque arête dirigée u-v, le sommet dans vient avant dans dans la commande.

Note: Le tri topologique d'un graphe n'est pas possible si le graphe n'est pas un JOUR .



Exemple:

Saisir: Graphique :

liste de tableaux triée java
exemple

Exemple



Sortir: 5 4 2 3 1 0
Explication: Le premier sommet du tri topologique est toujours un sommet avec un degré entrant de 0 (un sommet sans arêtes entrantes). Un tri topologique du graphe suivant est 5 4 2 3 1 0. Il peut y avoir plusieurs tris topologiques pour un graphe. Un autre tri topologique du graphe suivant est 4 5 2 3 1 0.

Pratique recommandéeSolution basée sur DFS pour trouver un tri topologique a déjà été discuté.

L'ordre topologique peut ne pas être unique :

Tri topologique est un problème de dépendance dans lequel l'achèvement d'une tâche dépend de l'achèvement de plusieurs autres tâches dont l'ordre peut varier. Comprenons ce concept à travers un exemple :



Supposons que notre tâche soit d'atteindre notre école et que pour y arriver, nous devons d'abord nous habiller. Les dépendances au port de vêtements sont indiquées dans le graphique de dépendance ci-dessous. Par exemple, vous ne pouvez pas porter de chaussures avant de porter des chaussettes.

1

À partir de l'image ci-dessus, vous auriez déjà réalisé qu'il existe plusieurs façons de s'habiller, l'image ci-dessous montre certaines de ces façons.

2

Pouvez-vous énumérer tous les ordres topologiques possibles de s'habiller pour le graphique de dépendance ci-dessus ?

différence de dates dans Excel

Algorithme de tri topologique utilisant DFS :

Voici un algorithme étape par étape pour le tri topologique à l’aide de Depth First Search (DFS) :

  • Créez un graphique avec n sommets et m -bords dirigés.
  • Initialiser une pile et un tableau de taille visité n .
  • Pour chaque sommet non visité du graphique, procédez comme suit :
    • Appelez la fonction DFS avec le sommet comme paramètre.
    • Dans la fonction DFS, marquez le sommet comme visité et appelez récursivement la fonction DFS pour tous les voisins non visités du sommet.
    • Une fois que tous les voisins ont été visités, poussez le sommet sur la pile.
  • Après tout, les sommets ont été visités, extrayez les éléments de la pile et ajoutez-les à la liste de sortie jusqu'à ce que la pile soit vide.
  • La liste résultante est l’ordre topologique du graphe.

Illustration de l'algorithme de tri topologique :

L'image ci-dessous est une illustration de l'approche ci-dessus :

Tri topologique

Flux de travail global du tri topologique

Étape 1:

  • Nous démarrons DFS à partir du nœud 0 car il n'a aucun nœud entrant
  • Nous poussons le nœud 0 dans la pile et passons au nœud suivant ayant un nombre minimum de nœuds adjacents, c'est-à-dire le nœud 1.

déposer

Étape 2:

  • Dans cette étape, comme il n'y a pas de nœud adjacent à ce nœud, poussez le nœud 1 dans la pile et passez au nœud suivant.

déposer

Étape 3:

  • Dans cette étape, nous choisissons le noeud 2 car il a un nombre minimum de noeuds adjacents après 0 et 1 .
  • Nous appelons DFS pour le nœud 2 et poussons tous les nœuds qui proviennent du nœud 2 dans l'ordre inverse.
  • Alors appuyez sur 3 puis appuyez sur 2 .

déposer

Étape 4:

  • Nous appelons maintenant DFS pour le nœud 4
  • Parce que 0 et 1 sont déjà présents dans la pile, nous poussons simplement le nœud 4 dans la pile et revenons.

déposer

Étape 5 :

  • Dans cette étape, parce que tous les nœuds adjacents de 5 sont déjà dans la pile, nous poussons le nœud 5 dans la pile et revenons.

déposer

Étape 6 : Il s'agit de la dernière étape du tri topologique dans laquelle nous extrayons tous les éléments de la pile et les imprimons dans cet ordre.

Vous trouverez ci-dessous la mise en œuvre de l’approche ci-dessus :

C++
#include  using namespace std; // Function to perform DFS and topological sorting void topologicalSortUtil(int v, vector>& adj, vecteur & visité, pile & Stack) { // Marque le nœud actuel comme visité visité[v] = true;  // Récurrence pour tous les sommets adjacents for (int i : adj[v]) { if (!visited[i]) topologicalSortUtil(i, adj, visited, Stack);  } // Poussez le sommet actuel vers la pile qui stocke le résultat Stack.push(v); } // Fonction pour effectuer un tri topologique void topologicalSort(vecteur>& adj, int V) { pile Empiler; // Pile pour stocker le vecteur résultat visité(V, faux);  // Appelez la fonction d'assistance récursive pour stocker // Tri topologique en commençant à partir de tous les sommets un par // un pour (int i = 0; i< V; i++) {  if (!visited[i])  topologicalSortUtil(i, adj, visited, Stack);  }  // Print contents of stack  while (!Stack.empty()) {  cout << Stack.top() << ' ';  Stack.pop();  } } int main() {  // Number of nodes  int V = 4;  // Edges  vector> bords = { { 0, 1 }, { 1, 2 }, { 3, 1 }, { 3, 2 } } ;  // Graphique représenté sous forme de vecteur de liste de contiguïté> adj(V);  for (auto i : bords) { adj[i[0]].push_back(i[1]);  } cout<< 'Topological sorting of the graph: ';  topologicalSort(adj, V);  return 0; }>
Java
import java.util.*; public class TopologicalSort {  // Function to perform DFS and topological sorting  static void  topologicalSortUtil(int v, List> adj, boolean[] visité, Stack stack) { // Marque le nœud actuel comme visité visited[v] = true;  // Récurrence pour tous les sommets adjacents for (int i : adj.get(v)) { if (!visited[i]) topologicalSortUtil(i, adj, visited, stack);  } // Poussez le sommet actuel vers la pile qui stocke le // résultat stack.push(v);  } // Fonction pour effectuer un tri topologique static void topologicalSort(List> adj, int V) { // Pile pour stocker le résultat Stack pile = nouvelle pile();  boolean[] visité = new boolean[V];  // Appelez la fonction d'assistance récursive pour stocker // Tri topologique en commençant à partir de tous les sommets un // par un pour (int i = 0; i< V; i++) {  if (!visited[i])  topologicalSortUtil(i, adj, visited, stack);  }  // Print contents of stack  System.out.print(  'Topological sorting of the graph: ');  while (!stack.empty()) {  System.out.print(stack.pop() + ' ');  }  }  // Driver code  public static void main(String[] args)  {  // Number of nodes  int V = 4;  // Edges  List> bords = new ArrayList();  bords.add(Arrays.asList(0, 1));  bords.add(Arrays.asList(1, 2));  bords.add(Arrays.asList(3, 1));  bords.add(Arrays.asList(3, 2));  // Graphique représenté sous forme de liste de contiguïté> adj = nouveau ArrayList(V);  pour (int je = 0; je< V; i++) {  adj.add(new ArrayList());  }  for (List i : bords) { adj.get(i.get(0)).add(i.get(1));  } tri topologique (adj, V);  } }>
Python3
def topologicalSortUtil(v, adj, visited, stack): # Mark the current node as visited visited[v] = True # Recur for all adjacent vertices for i in adj[v]: if not visited[i]: topologicalSortUtil(i, adj, visited, stack) # Push current vertex to stack which stores the result stack.append(v) # Function to perform Topological Sort def topologicalSort(adj, V): # Stack to store the result stack = [] visited = [False] * V # Call the recursive helper function to store # Topological Sort starting from all vertices one by # one for i in range(V): if not visited[i]: topologicalSortUtil(i, adj, visited, stack) # Print contents of stack print('Topological sorting of the graph:', end=' ') while stack: print(stack.pop(), end=' ') # Driver code if __name__ == '__main__': # Number of nodes V = 4 # Edges edges = [[0, 1], [1, 2], [3, 1], [3, 2]] # Graph represented as an adjacency list adj = [[] for _ in range(V)] for i in edges: adj[i[0]].append(i[1]) topologicalSort(adj, V)>
C#
using System; using System.Collections.Generic; class Program {  // Function to perform DFS and topological sorting  static void TopologicalSortUtil(int v,  List> adj, bool[] visité, Stack stack) { // Marque le nœud actuel comme visité visited[v] = true;  // Récurrence pour tous les sommets adjacents foreach(int i in adj[v]) { if (!visited[i]) TopologicalSortUtil(i, adj, visited, stack);  } // Poussez le sommet actuel vers la pile qui stocke le // résultat stack.Push(v);  } // Fonction pour effectuer un tri topologique static void TopologicalSort(List> adj, int V) { // Pile pour stocker le résultat Stack pile = nouvelle pile ();  bool[] visité = new bool[V];  // Appelez la fonction d'assistance récursive pour stocker // Tri topologique en commençant à partir de tous les sommets un // par un pour (int i = 0; i< V; i++) {  if (!visited[i])  TopologicalSortUtil(i, adj, visited, stack);  }  // Print contents of stack  Console.Write('Topological sorting of the graph: ');  while (stack.Count>0) { Console.Write(stack.Pop() + ' ');  } } // Code du pilote static void Main(string[] args) { // Nombre de nœuds int V = 4;  // Liste des bords> bords = nouvelle liste>{ nouvelle liste { 0, 1 }, nouvelle liste { 1, 2 }, nouvelle liste { 3, 1 }, nouvelle liste { 3, 2 } } ;  // Graphique représenté sous forme de liste de contiguïté> adj = nouvelle liste>();  pour (int je = 0; je< V; i++) {  adj.Add(new List ());  } foreach(Liste i dans les bords) { adj[i[0]].Add(i[1]);  } Tri topologique (adj, V);  } }>
Javascript
// Function to perform DFS and topological sorting function topologicalSortUtil(v, adj, visited, stack) {  // Mark the current node as visited  visited[v] = true;  // Recur for all adjacent vertices  for (let i of adj[v]) {  if (!visited[i])  topologicalSortUtil(i, adj, visited, stack);  }  // Push current vertex to stack which stores the result  stack.push(v); } // Function to perform Topological Sort function topologicalSort(adj, V) {  // Stack to store the result  let stack = [];  let visited = new Array(V).fill(false);  // Call the recursive helper function to store  // Topological Sort starting from all vertices one by  // one  for (let i = 0; i < V; i++) {  if (!visited[i])  topologicalSortUtil(i, adj, visited, stack);  }  // Print contents of stack  console.log('Topological sorting of the graph: ');  while (stack.length>0) { console.log(stack.pop() + ' ');  } } // Code du pilote (() => { // Nombre de nœuds const V = 4; // Bords const bords = [[0, 1], [1, 2], [3, 1], [3, 2]]; // Graphique représenté comme une liste de contiguïté const adj = Array.from({ length: V }, () => []); (i[1]); } tri topologique (adj, V })();>

Sortir
Topological sorting of the graph: 3 0 1 2>

Complexité temporelle : O(V+E). L'algorithme ci-dessus est simplement DFS avec une pile supplémentaire. La complexité temporelle est donc la même que celle de DFS
Espace auxiliaire : O(V). L'espace supplémentaire est nécessaire pour la pile

Tri topologique à l'aide de BFS :

inverser la chaîne java
C++
#include  #include  #include  using namespace std; // Class to represent a graph class Graph {  int V; // No. of vertices  list *adj; // Pointeur vers un tableau contenant // des listes de contiguïté public: Graph(int V); // Constructeur void addEdge(int v, int w); // Fonction pour ajouter une arête au graphique void topologicalSort(); // imprime un tri topologique de // le graphe complet }; Graphique :: Graphique (int V) { this->V = V;  adj = nouvelle liste [V] ; } void Graph::addEdge(int v, int w) { adj[v].push_back(w); // Ajoute w à la liste de v. } // Fonction pour effectuer un tri topologique void Graph::topologicalSort() { // Crée un vecteur pour stocker le vecteur en degrés de tous les sommets in_degree(V, 0);  // Parcourez les listes de contiguïté pour remplir in_degree des // sommets pour (int v = 0; v< V; ++v) {  for (auto const& w : adj[v])  in_degree[w]++;  }  // Create a queue and enqueue all vertices with  // in-degree 0  queue q;  pour (int je = 0; je< V; ++i) {  if (in_degree[i] == 0)  q.push(i);  }  // Initialize count of visited vertices  int count = 0;  // Create a vector to store topological order  vector top_order;  // Un par un, retirez les sommets de la file d'attente et mettez-les en file d'attente // les sommets adjacents si le degré d'adjacent devient 0 while (!q.empty()) { // Extrayez le début de la file d'attente (ou effectuez le retrait de la file d'attente) // et ajoutez-le à ordre topologique int u = q.front();  q.pop();  top_order.push_back(u);  // Parcourir tous ses nœuds voisins // du nœud u retiré de la file d'attente et diminuer leur degré // de 1 liste ::itérateur itr;  for (itr = adj[u].begin(); itr != adj[u].end(); ++itr) // Si in-degree devient zéro, ajoutez-le à la file d'attente if (--in_degree[*itr ] == 0) q.push(*itr);  compte++;  } // Vérifiez s'il y a eu un cycle if (count != V) { cout<< 'Graph contains cycle
';  return;  }  // Print topological order  for (int i : top_order)  cout << i << ' '; } // Driver code int main() {  // Create a graph given in the above diagram  Graph g(6);  g.addEdge(5, 2);  g.addEdge(5, 0);  g.addEdge(4, 0);  g.addEdge(4, 1);  g.addEdge(2, 3);  g.addEdge(3, 1);  cout << 'Following is a Topological Sort of the given '  'graph
';  g.topologicalSort();  return 0; }>
Java
import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; // Class to represent a graph class Graph {  private int V; // No. of vertices  private ArrayList [] adj; // Liste de contiguïté // représentation du // graphe // Constructeur Graph(int V) { this.V = V;  adj = nouveau ArrayList[V];  pour (int je = 0; je< V; ++i)  adj[i] = new ArrayList();  }  // Function to add an edge to the graph  void addEdge(int v, int w)  {  adj[v].add(w); // Add w to v’s list.  }  // Function to perform Topological Sort  void topologicalSort()  {  // Create an array to store in-degree of all  // vertices  int[] inDegree = new int[V];  // Calculate in-degree of each vertex  for (int v = 0; v < V; ++v) {  for (int w : adj[v]) {  inDegree[w]++;  }  }  // Create a queue and enqueue all vertices with  // in-degree 0  Queue q = nouvelle LinkedList();  pour (int je = 0; je< V; ++i) {  if (inDegree[i] == 0)  q.add(i);  }  // Initialize count of visited vertices  int count = 0;  // Create an ArrayList to store topological order  ArrayList topOrder = new ArrayList();  // Un par un, retirez les sommets de la file d'attente et // mettez en file d'attente les sommets adjacents si le degré de // adjacent devient 0 while (!q.isEmpty()) { // Extrayez le début de la file d'attente et ajoutez-le à // l'ordre topologique int u = q.poll();  topOrder.add(u);  compte++;  // Parcourez tous ses nœuds voisins du // nœud u retiré de la file d'attente et diminuez leur degré // de 1 for (int w : adj[u]) { // Si le degré devient zéro, ajoutez-le // à la file d'attente if (--inDegree[w] == 0) q.add(w);  } } // Vérifiez s'il y a eu un cycle if (count != V) { System.out.println('Graph contain cycle');  retour;  } // Imprimer l'ordre topologique pour (int i : topOrder) System.out.print(i + ' ');  } } // Code du pilote public class Main { public static void main(String[] args) { // Crée un graphique donné dans le diagramme ci-dessus Graph g = new Graph(6);  g.addEdge(5, 2);  g.addEdge(5, 0);  g.addEdge(4, 0);  g.addEdge(4, 1);  g.addEdge(2, 3);  g.addEdge(3, 1);  System.out.println( 'Voici un tri topologique du graphique donné');  g.topologicalSort();  } }>
Python3
from collections import defaultdict class Graph: def __init__(self, vertices): # Number of vertices self.V = vertices # Dictionary to store adjacency lists self.adj = defaultdict(list) def addEdge(self, u, v): # Function to add an edge to the graph self.adj[u].append(v) def topologicalSort(self): # Function to perform Topological Sort # Create a list to store in-degree of all vertices in_degree = [0] * self.V # Traverse adjacency lists to fill in_degree of vertices for i in range(self.V): for j in self.adj[i]: in_degree[j] += 1 # Create a queue and enqueue all vertices with in-degree 0 q = [] for i in range(self.V): if in_degree[i] == 0: q.append(i) # Initialize count of visited vertices count = 0 # Create a list to store topological order top_order = [] # One by one dequeue vertices from queue and enqueue # adjacent vertices if in-degree of adjacent becomes 0 while q: # Extract front of queue (or perform dequeue) # and add it to topological order u = q.pop(0) top_order.append(u) # Iterate through all its neighbouring nodes # of dequeued node u and decrease their in-degree # by 1 for node in self.adj[u]: # If in-degree becomes zero, add it to queue in_degree[node] -= 1 if in_degree[node] == 0: q.append(node) count += 1 # Check if there was a cycle if count != self.V: print('Graph contains cycle') return # Print topological order print('Topological Sort:', top_order) # Driver code if __name__ == '__main__': # Create a graph given in the above diagram g = Graph(6) g.addEdge(5, 2) g.addEdge(5, 0) g.addEdge(4, 0) g.addEdge(4, 1) g.addEdge(2, 3) g.addEdge(3, 1) print('Following is a Topological Sort of the given graph') g.topologicalSort()>
Javascript
// Class to represent a graph class Graph {  constructor(V) {  this.V = V; // No. of vertices  this.adj = new Array(V); // Array containing adjacency lists  for (let i = 0; i < V; i++) {  this.adj[i] = [];  }  }  // Function to add an edge to the graph  addEdge(v, w) {  this.adj[v].push(w); // Add w to v’s list.  }  // Function to perform Topological Sort  topologicalSort() {  // Create a array to store in-degree of all vertices  let inDegree = new Array(this.V).fill(0);  // Traverse adjacency lists to fill inDegree of vertices  for (let v = 0; v < this.V; v++) {  for (let w of this.adj[v]) {  inDegree[w]++;  }  }  // Create a queue and enqueue all vertices with in-degree 0  let queue = [];  for (let i = 0; i < this.V; i++) {  if (inDegree[i] === 0) {  queue.push(i);  }  }  // Initialize count of visited vertices  let count = 0;  // Create an array to store topological order  let topOrder = [];  // One by one dequeue vertices from queue and enqueue  // adjacent vertices if in-degree of adjacent becomes 0  while (queue.length !== 0) {  // Extract front of queue and add it to topological order  let u = queue.shift();  topOrder.push(u);  // Iterate through all its neighboring nodes  // of dequeued node u and decrease their in-degree by 1  for (let w of this.adj[u]) {  // If in-degree becomes zero, add it to queue  if (--inDegree[w] === 0) {  queue.push(w);  }  }  count++;  }  // Check if there was a cycle  if (count !== this.V) {  console.log('Graph contains cycle');  return;  }  // Print topological order  console.log('Topological Sort of the given graph:');  console.log(topOrder.join(' '));  } } // Driver code // Create a graph given in the above diagram let g = new Graph(6); g.addEdge(5, 2); g.addEdge(5, 0); g.addEdge(4, 0); g.addEdge(4, 1); g.addEdge(2, 3); g.addEdge(3, 1); console.log('Following is a Topological Sort of the given graph:'); g.topologicalSort(); //This code is contributed by Utkarsh>

Sortir
Following is a Topological Sort of the given graph 4 5 2 0 3 1>

Complexité temporelle :

La complexité temporelle pour la construction du graphe est O(V + E), où V est le nombre de sommets et E est le nombre d'arêtes.

comment injecter une classe abstraite fictive

La complexité temporelle pour effectuer un tri topologique à l'aide de BFS est également O(V + E), où V est le nombre de sommets et E est le nombre d'arêtes. En effet, chaque sommet et chaque arête sont visités une fois pendant le parcours BFS.

Complexité spatiale :

La complexité spatiale pour stocker le graphe à l'aide d'une liste de contiguïté est O(V + E), où V est le nombre de sommets et E est le nombre d'arêtes.

Un espace supplémentaire est utilisé pour stocker le degré des sommets, ce qui nécessite un espace O(V).

Une file d'attente est utilisée pour le parcours BFS, qui peut contenir au plus V sommets. Ainsi, la complexité spatiale de la file d’attente est O(V).

Dans l’ensemble, la complexité spatiale de l’algorithme est O(V + E) en raison du stockage du graphique, du tableau en degrés et de la file d’attente.

En résumé, la complexité temporelle de l'implémentation fournie est O(V + E), et la complexité spatiale est également O(V + E).

Note: Ici, nous pouvons également utiliser un tableau au lieu de la pile. Si le tableau est utilisé, imprimez les éléments dans l'ordre inverse pour obtenir le tri topologique.

Avantages du tri topologique :

  • Aide à planifier des tâches ou des événements en fonction des dépendances.
  • Détecte les cycles dans un graphique orienté.
  • Efficace pour résoudre des problèmes avec des contraintes de préséance.

Inconvénients du tri topologique :

  • Applicable uniquement aux graphiques acycliques dirigés (DAG), ne convient pas aux graphiques cycliques.
  • Peut ne pas être unique, plusieurs ordres topologiques valides peuvent exister.
  • Inefficace pour les grands graphiques comportant de nombreux nœuds et arêtes.

Applications du tri topologique :

  • Planification des tâches et gestion de projet.
  • Résolution des dépendances dans les systèmes de gestion de packages.
  • Détermination de l'ordre de compilation dans les systèmes de construction de logiciels.
  • Détection de blocage dans les systèmes d'exploitation.
  • Programmation des cours dans les universités.

Articles Liés:

  • Algorithme de Kahn pour le tri topologique
  • Toutes les sortes topologiques d'un graphe acyclique dirigé