Topologické řazení pro Řízený acyklický graf (DAG) je lineární uspořádání vrcholů takové, že pro každou směrovanou hranu u-v je vrchol v přichází dříve v v objednávce.
Poznámka: Topologické řazení pro graf není možné, pokud graf není a DEN .
Příklad:
Doporučená praxeŘešení založené na DFS pro nalezení topologického řazení již byla projednána.Vstup: Graf :
Příklad
Výstup: 5 4 2 3 1 0
Vysvětlení: První vrchol v topologickém třídění je vždy vrchol s in-stupně 0 (vrchol bez vstupních hran). Topologické řazení následujícího grafu je 5 4 2 3 1 0. Pro graf může být více než jedno topologické řazení. Další topologické řazení následujícího grafu je 4 5 2 3 1 0.
java hashmap
Topologické pořadí nemusí být jedinečné:
Topologické třídění je problém závislosti, ve kterém dokončení jednoho úkolu závisí na dokončení několika dalších úkolů, jejichž pořadí se může lišit. Pojďme pochopit tento koncept na příkladu:
Předpokládejme, že naším úkolem je dostat se do naší školy a abychom se tam dostali, musíme se nejprve obléknout. Závislosti na nošení oblečení jsou uvedeny v níže uvedeném grafu závislostí. Například nemůžete nosit boty před nošením ponožek.
Z výše uvedeného obrázku byste si již uvědomili, že existuje několik způsobů, jak se obléknout, obrázek níže ukazuje některé z těchto způsobů.
Můžete seznam všechna možná topologická uspořádání oblékání do výše uvedeného grafu závislosti?
Algoritmus pro topologické třídění pomocí DFS:
Zde je krok za krokem algoritmus pro topologické třídění pomocí hloubkového prvního vyhledávání (DFS):
- Vytvořte graf pomocí n vrcholy a m - směrované hrany.
- Inicializujte zásobník a navštívené pole velikosti n .
- Pro každý nenavštívený vrchol v grafu proveďte následující:
- Volejte funkci DFS s vrcholem jako parametrem.
- Ve funkci DFS označte vrchol jako navštívený a rekurzivně zavolejte funkci DFS pro všechny nenavštívené sousedy vrcholu.
- Jakmile navštívíte všechny sousedy, zatlačte vrchol na hromádku.
- Koneckonců, vrcholy byly navštíveny, vytahujte prvky ze zásobníku a připojujte je k výstupnímu seznamu, dokud není zásobník prázdný.
- Výsledný seznam je topologicky seřazené pořadí grafu.
Ilustrace Algoritmus topologického řazení:
Níže uvedený obrázek je ilustrací výše uvedeného přístupu:

Celkový pracovní postup topologického třídění
Krok 1:
- Spustíme DFS od uzlu 0, protože nemá žádné příchozí uzly
- Vložíme uzel 0 do zásobníku a přesuneme se na další uzel s minimálním počtem sousedních uzlů, tj. uzel 1.
Krok 2:
- V tomto kroku, protože u tohoto uzlu není žádný soused, zatlačte na uzel 1 v zásobníku a přesuňte se na další uzel.
Krok 3:
- V tomto kroku zvolíme uzel 2, protože má minimální počet sousedních uzlů po 0 a 1.
- Zavoláme DFS pro uzel 2 a posuneme všechny uzly, které přicházejí v průchodu z uzlu 2, v opačném pořadí.
- Takže stiskněte 3 a poté stiskněte 2 .
vyměnit všeKrok 4:
- Nyní voláme DFS pro uzel 4
- Protože 0 a 1 jsou již v zásobníku přítomny, tak jen zatlačíme uzel 4 v zásobníku a vrátíme se.
Krok 5:
- V tomto kroku, protože všechny sousední uzly 5 jsou již v zásobníku, zatlačíme uzel 5 do zásobníku a vrátíme se.
Krok 6: Toto je poslední krok topologického třídění, ve kterém vyjmeme všechny prvky ze zásobníku a vytiskneme je v tomto pořadí.
Níže je uvedena implementace výše uvedeného přístupu:
C++ #include using namespace std; // Function to perform DFS and topological sorting void topologicalSortUtil(int v, vector>& adj, vektor & navštívil, stohovat & Stack) { // Označí aktuální uzel jako navštívený[v] = true; // Opakuje se pro všechny sousední vrcholy pro (int i : adj[v]) { if (!navštívené[i]) topologicalSortUtil(i, adj, visited, Stack); } // Přesune aktuální vrchol do zásobníku, který uloží výsledek Stack.push(v); } // Funkce pro provedení topologického třídění void topologicalSort(vector>& adj, int V) { zásobník Zásobník; // Stack pro uložení výsledného vektoru navštívil(V, nepravda); // Volání rekurzivní pomocné funkce pro uložení // Topologické řazení začínající od všech vrcholů jeden po // jeden pro (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> hrany = { { 0, 1 }, { 1, 2 }, { 3, 1 }, { 3, 2 } }; // Graf reprezentovaný jako vektor seznamu sousedství> adj(V); for (auto i : hrany) { adj[i[0]].push_back(i[1]); } cout<< 'Topological sorting of the graph: '; topologicalSort(adj, V); return 0; }>
Jáva import java.util.*; public class TopologicalSort { // Function to perform DFS and topological sorting static void topologicalSortUtil(int v, List> adj, boolean[] navštíveno, zásobník stack) { // Označí aktuální uzel jako navštívený[v] = true; // Opakuje se pro všechny sousední vrcholy for (int i : adj.get(v)) { if (!visited[i]) topologicalSortUtil(i, adj, visited, stack); } // Přesune aktuální vrchol do zásobníku, který uloží // výsledek stack.push(v); } // Funkce pro provedení topologického třídění statického void topologicalSort(List> adj, int V) { // Stack pro uložení výsledku Stack stack = new Stack(); boolean[] navštíveno = new boolean[V]; // Volání rekurzivní pomocné funkce pro uložení // Topologické řazení počínaje všemi vrcholy po jedné // po jedné pro (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> hrany = new ArrayList(); edge.add(Arrays.asList(0, 1)); edge.add(Arrays.asList(1, 2)); edge.add(Arrays.asList(3, 1)); edge.add(Arrays.asList(3, 2)); // Graf reprezentovaný jako seznam sousedství List> adj = new ArrayList(V); for (int i = 0; i< V; i++) { adj.add(new ArrayList()); } for (List i : hrany) { adj.get(i.get(0)).add(i.get(1)); } topologicalSort(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[] navštíveno, zásobník stack) { // Označí aktuální uzel jako navštívený[v] = true; // Opakuje se pro všechny sousední vrcholy foreach(int i v adj[v]) { if (!navštívené[i]) TopologicalSortUtil(i, adj, visited, stack); } // Přesune aktuální vrchol do zásobníku, který uloží // výsledek stack.Push(v); } // Funkce pro provedení statického topologického třídění void TopologicalSort(List> adj, int V) { // Stack pro uložení výsledku Stack zásobník = nový zásobník (); bool[] navštíveno = nový bool[V]; // Volání rekurzivní pomocné funkce pro uložení // Topologické řazení počínaje všemi vrcholy po jedné // po jedné pro (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() + ' '); } } // Kód ovladače static void Main(string[] args) { // Počet uzlů int V = 4; // Seznam hran> hrany = nový seznam>{ nový seznam { 0, 1 }, nový seznam { 1, 2 }, nový seznam { 3, 1 }, nový seznam { 3, 2 } }; // Graf reprezentovaný jako seznam sousedství List> adj = nový seznam>(); for (int i = 0; i< V; i++) { adj.Add(new List ()); } foreach(Seznam i v hranách) { adj[i[0]].Add(i[1]); } TopologicalSort(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() + ' '); } } // Kód ovladače (() => { // Počet uzlů const V = 4; // Hrany const hran = [[0, 1], [1, 2], [3, 1], [3, 2]] // Graf reprezentovaný jako seznam sousedství const adj = Array.from({ délka: V }, () => [] for (ať i hran) { adj[i[0]].push); (i[1] } topologicalSort(adj, V)();>
Výstup
Topological sorting of the graph: 3 0 1 2>
Časová náročnost: O(V+E). Výše uvedený algoritmus je jednoduše DFS s extra zásobníkem. Časová složitost je tedy stejná jako u DFS
Pomocný prostor: O(V). Další prostor je potřeba pro zásobník
Topologické řazení pomocí BFS:
C++ #include #include #include using namespace std; // Class to represent a graph class Graph { int V; // No. of vertices list * adj; // Ukazatel na pole obsahující // seznamy sousedství public: Graph(int V); // Konstruktor void addEdge(int v, int w); // Funkce pro přidání hrany do grafu void topologicalSort(); // vypíše topologický druh // kompletního grafu }; Graph::Graph(int V) { this->V = V; adj = nový seznam [PROTI]; } void Graph::addEdge(int v, int w) { adj[v].push_back(w); // Přidejte w do seznamu v. } // Funkce k provedení topologického třídění void Graph::topologicalSort() { // Vytvořte vektor pro uložení vektoru všech vrcholů ve stupních in_degree(V, 0); // Procházet seznamy sousedství k vyplnění in_degree // vrcholů pro (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; for (int i = 0; i< 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; // Jeden po druhém vyřaďte vrcholy z fronty a zařaďte do fronty // sousední vrcholy, pokud se in-stupeň sousedního stane 0 while (!q.empty()) { // Extrahujte přední část fronty (nebo proveďte dequeue) // a přidejte ji do topologické pořadí int u = q.front(); q.pop(); top_order.push_back(u); // Iterujte všechny jeho sousední uzly // vyřazeného uzlu u a snižte jejich stupeň // o 1 seznam ::iterator itr; for (itr = adj[u].begin(); itr != adj[u].end(); ++itr) // Pokud se in-degree stane nulou, přidejte jej do fronty if (--in_degree[*itr ] == 0) q.push(*itr); počet++; } // Kontrola, zda došlo k cyklu 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; }>
Jáva 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; // Seznam sousedství // reprezentace // grafu // Konstruktor Graph(int V) { this.V = V; adj = new ArrayList[V]; for (int i = 0; i< 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 = new LinkedList(); for (int i = 0; i< 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(); // Jeden po druhém vyřaďte vrcholy z fronty a // zařaďte sousední vrcholy, pokud se in-stupeň // sousední stane 0 while (!q.isEmpty()) { // Extrahujte přední část fronty a přidejte ji do // topologického pořadí int u = q.poll(); topOrder.add(u); počet++; // Iterujte všechny jeho sousední uzly // vyřazeného uzlu u a snižte jejich stupeň // o 1 for (int w : adj[u]) { // Pokud se in-degree stane nulou, přidejte jej do // fronty if (--inDegree[w] == 0) q.add(w); } } // Kontrola, zda došlo k cyklu if (počet != V) { System.out.println('Graf obsahuje cyklus'); vrátit se; } // Tisk topologického pořadí pro (int i : topOrder) System.out.print(i + ' '); } } // Kód ovladače public class Main { public static void main(String[] args) { // Vytvořte graf uvedený ve výše uvedeném diagramu 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( 'Následuje topologické řazení daného grafu'); 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>
Výstup
Following is a Topological Sort of the given graph 4 5 2 0 3 1>
Časová náročnost:
Časová složitost pro konstrukci grafu je O(V + E), kde V je počet vrcholů a E je počet hran.
Časová složitost pro provádění topologického třídění pomocí BFS je také O(V + E), kde V je počet vrcholů a E je počet hran. Je to proto, že každý vrchol a každá hrana jsou během procházení BFS navštíveny jednou.
Prostorová složitost:
Prostorová složitost pro uložení grafu pomocí seznamu sousedství je O(V + E), kde V je počet vrcholů a E je počet hran.
Další prostor se používá pro uložení stupňů vrcholů, což vyžaduje O(V) prostor.
Pro BFS traversal se používá fronta, která může obsahovat maximálně V vertexů. Prostorová složitost pro frontu je tedy O(V).
Celkově je prostorová složitost algoritmu O(V + E) v důsledku uložení grafu, in-degree pole a fronty.
Stručně řečeno, časová složitost poskytované implementace je O(V + E) a prostorová složitost je také O(V + E).
Poznámka: Zde také můžeme místo zásobníku použít pole. Pokud je pole použito, vytiskněte prvky v opačném pořadí, abyste získali topologické řazení.
Výhody topologického řazení:
- Pomáhá při plánování úkolů nebo událostí na základě závislostí.
- Detekuje cykly v orientovaném grafu.
- Efektivní pro řešení problémů s omezeními priority.
Nevýhody topologického řazení:
- Platí pouze pro směrované acyklické grafy (DAG), není vhodné pro cyklické grafy.
- Nemusí být jedinečné, může existovat více platných topologických uspořádání.
- Neefektivní pro velké grafy s mnoha uzly a hranami.
Aplikace topologického řazení:
- Plánování úkolů a řízení projektů.
- Řešení závislostí v systémech správy balíčků.
- Určení pořadí kompilace v systémech sestavování softwaru.
- Detekce uváznutí v operačních systémech.
- Rozvrh kurzů na univerzitách.
Související články:
- Kahnův algoritmus pro topologické třídění
- Všechny topologické druhy řízeného acyklického grafu