Skip to content

Commit 97da901

Browse files
authored
Create README.md
1 parent 7d30c9f commit 97da901

File tree

1 file changed

+377
-0
lines changed
  • 23 - Graph Data Structure Problems/09 - Topological Sort using DFS

1 file changed

+377
-0
lines changed
Lines changed: 377 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
<h1 align='center'>Topological - Sort - Using - DFS</h1>
2+
3+
## Problem Statement
4+
5+
**Problem URL :** [Topological Sort using DFS](https://www.geeksforgeeks.org/topological-sorting/)
6+
7+
### **What is Topological Sort?**
8+
- Topological sorting is a linear ordering of vertices in a **directed acyclic graph (DAG)** such that for every directed edge `U → V`, vertex `U` appears before vertex `V` in the ordering.
9+
- **Applications**:
10+
1. Task scheduling (e.g., compiling tasks with dependencies).
11+
2. Course prerequisite planning.
12+
3. Resolving symbol dependencies in linkers.
13+
14+
15+
#### **Example: Understanding Topological Sort**
16+
17+
#### Graph:
18+
Consider a directed graph with 4 nodes and the following edges:
19+
```
20+
0 → 1
21+
1 → 2
22+
3 → 1
23+
3 → 2
24+
```
25+
26+
#### Representation:
27+
**Adjacency List**:
28+
```
29+
0 → [1]
30+
1 → [2]
31+
3 → [1, 2]
32+
```
33+
34+
#### **Steps for Topological Sort**:
35+
1. Identify nodes with no incoming edges (start points).
36+
2. Use **Depth First Search (DFS)** to explore the graph.
37+
3. When a node has no more unvisited neighbors, add it to the stack.
38+
4. Continue until all nodes are visited.
39+
5. The stack contains the nodes in **reverse topological order**.
40+
41+
**Result for the given graph**:
42+
- One valid topological ordering: `3 0 1 2`.
43+
44+
45+
### **Approach: DFS for Topological Sorting**
46+
47+
#### **Key Steps**:
48+
1. **Initialization**:
49+
- Maintain a `visited` array to track visited nodes.
50+
- Use a stack to store the topological order.
51+
52+
2. **DFS Traversal**:
53+
- Visit all neighbors of the current node recursively.
54+
- When there are no more neighbors, add the current node to the stack.
55+
56+
3. **Retrieve Order**:
57+
- Pop all elements from the stack to get the topological order.
58+
59+
60+
### Problem Solution
61+
```cpp
62+
#include <bits/stdc++.h>
63+
using namespace std;
64+
65+
// Function to perform DFS and topological sorting
66+
void topologicalSortUtil(int node, vector<vector<int> >& adj, vector<bool>& visited, stack<int>& Stack){
67+
// Mark the current node as visited
68+
visited[node] = true;
69+
70+
// Recur for all adjacent vertices
71+
for (auto neighbour : adj[node]) if (!visited[neighbour]) topologicalSortUtil(neighbour, adj, visited, Stack);
72+
73+
// Push current vertex to stack which stores the result
74+
Stack.push(node);
75+
}
76+
77+
// Function to perform Topological Sort
78+
void topologicalSort(vector<vector<int> >& adj, int V){
79+
stack<int> Stack; // Stack to store the result
80+
vector<bool> visited(V, false);
81+
82+
// Call the recursive helper function to store
83+
// Topological Sort starting from all vertices one by
84+
// one
85+
for (int i = 0; i < V; i++) if (!visited[i]) topologicalSortUtil(i, adj, visited, Stack);
86+
87+
// Print contents of stack
88+
while (!Stack.empty()) {
89+
cout << Stack.top() << " ";
90+
Stack.pop();
91+
}
92+
}
93+
94+
int main()
95+
{
96+
97+
// Number of nodes
98+
int V = 4;
99+
100+
// Edges
101+
vector<vector<int> > edges = {{0, 1}, {1, 2}, {3, 1}, {3, 2}};
102+
103+
// Graph represented as an adjacency list
104+
vector<vector<int> > adj(V);
105+
106+
for (auto i : edges) adj[i[0]].push_back(i[1]);
107+
108+
cout << "Topological sorting of the graph: ";
109+
topologicalSort(adj, V);
110+
111+
return 0;
112+
}
113+
```
114+
115+
## Problem Solution Explanation
116+
117+
Let's go through the **Topological Sort code** line by line and explain each part with examples.
118+
119+
```cpp
120+
#include <bits/stdc++.h>
121+
using namespace std;
122+
```
123+
124+
- **`#include <bits/stdc++.h>`**:
125+
- Includes all standard C++ libraries (e.g., iostream, vector, stack).
126+
- Simplifies development by allowing us to use containers like `vector`, `stack`, etc.
127+
128+
- **`using namespace std;`**:
129+
- Avoids the need to prefix standard library functions and types (e.g., `std::vector`, `std::cout`).
130+
131+
132+
### **Helper Function: `topologicalSortUtil`**
133+
134+
```cpp
135+
void topologicalSortUtil(int node, vector<vector<int>>& adj, vector<bool>& visited, stack<int>& Stack) {
136+
visited[node] = true;
137+
```
138+
139+
- **`void topologicalSortUtil(...)`**:
140+
- Helper function to perform **DFS**.
141+
- **Parameters**:
142+
1. `node`: The current node being processed.
143+
2. `adj`: Adjacency list representation of the graph.
144+
3. `visited`: Keeps track of nodes already visited.
145+
4. `Stack`: Stores nodes in reverse topological order.
146+
147+
- **`visited[node] = true;`**:
148+
- Marks the current node as visited to prevent revisiting.
149+
150+
151+
152+
```cpp
153+
for (auto neighbour : adj[node]) {
154+
if (!visited[neighbour]) {
155+
topologicalSortUtil(neighbour, adj, visited, Stack);
156+
}
157+
}
158+
```
159+
160+
- **`for (auto neighbour : adj[node]) {...}`**:
161+
- Iterates over all neighbors of the current node using the adjacency list.
162+
- **`if (!visited[neighbour])`**:
163+
- Checks if the neighbor is unvisited. If so, calls `topologicalSortUtil` recursively.
164+
165+
**Example**:
166+
For the graph:
167+
```
168+
0 → [1]
169+
1 → [2]
170+
```
171+
If `node = 0`, then `adj[0] = [1]`. The function recursively processes `1`.
172+
173+
174+
175+
```cpp
176+
Stack.push(node);
177+
}
178+
```
179+
180+
- **`Stack.push(node);`**:
181+
- Once all neighbors of the node are visited, the node is pushed to the stack.
182+
- This ensures that nodes with no dependencies are processed first.
183+
184+
**Example**:
185+
For the graph:
186+
```
187+
0 → [1]
188+
1 → [2]
189+
```
190+
Order of processing:
191+
- Visit `2`, push it to the stack.
192+
- Visit `1`, push it to the stack.
193+
- Visit `0`, push it to the stack.
194+
195+
196+
197+
### **Main Function: `topologicalSort`**
198+
199+
```cpp
200+
void topologicalSort(vector<vector<int>>& adj, int V) {
201+
stack<int> Stack;
202+
vector<bool> visited(V, false);
203+
```
204+
205+
- **`stack<int> Stack;`**:
206+
- Declares a stack to store nodes in topological order.
207+
208+
- **`vector<bool> visited(V, false);`**:
209+
- Initializes a `visited` array with `false` for all nodes, indicating that no nodes have been visited yet.
210+
211+
212+
213+
```cpp
214+
for (int i = 0; i < V; i++) {
215+
if (!visited[i]) {
216+
topologicalSortUtil(i, adj, visited, Stack);
217+
}
218+
}
219+
```
220+
221+
- **`for (int i = 0; i < V; i++)`**:
222+
- Loops through all nodes.
223+
- **`if (!visited[i]) {...}`**:
224+
- For unvisited nodes, calls `topologicalSortUtil` to perform DFS.
225+
226+
**Example**:
227+
For the graph:
228+
```
229+
0 → [1]
230+
1 → [2]
231+
3 → [1, 2]
232+
```
233+
- The loop starts with node `0`, calls `topologicalSortUtil` for `0`.
234+
- Next, it processes node `3`.
235+
236+
237+
238+
```cpp
239+
while (!Stack.empty()) {
240+
cout << Stack.top() << " ";
241+
Stack.pop();
242+
}
243+
}
244+
```
245+
246+
- **`while (!Stack.empty()) {...}`**:
247+
- Retrieves nodes from the stack one by one and prints them in topological order.
248+
- **`Stack.top()`**:
249+
- Returns the top element of the stack.
250+
- **`Stack.pop()`**:
251+
- Removes the top element from the stack.
252+
253+
**Example**:
254+
For the graph:
255+
```
256+
0 → [1]
257+
1 → [2]
258+
3 → [1, 2]
259+
```
260+
Stack content after processing:
261+
```
262+
3, 0, 1, 2
263+
```
264+
Output:
265+
```
266+
3 0 1 2
267+
```
268+
269+
270+
271+
### **Driver Code: `main`**
272+
273+
```cpp
274+
int main() {
275+
int V = 4;
276+
vector<vector<int>> edges = {{0, 1}, {1, 2}, {3, 1}, {3, 2}};
277+
```
278+
279+
- **`int V = 4;`**:
280+
- Defines the number of nodes (vertices) in the graph.
281+
282+
- **`vector<vector<int>> edges = {...};`**:
283+
- Represents directed edges between nodes.
284+
285+
**Example**:
286+
Edges `{0, 1}, {1, 2}, {3, 1}, {3, 2}` mean:
287+
```
288+
0 → 1
289+
1 → 2
290+
3 → 1, 2
291+
```
292+
293+
294+
295+
```cpp
296+
vector<vector<int>> adj(V);
297+
for (auto i : edges) {
298+
adj[i[0]].push_back(i[1]);
299+
}
300+
```
301+
302+
- **`vector<vector<int>> adj(V);`**:
303+
- Adjacency list representation of the graph.
304+
305+
- **`for (auto i : edges) {...}`**:
306+
- Converts the list of edges into an adjacency list.
307+
308+
**Example**:
309+
For edges `{0, 1}, {1, 2}, {3, 1}, {3, 2}`, adjacency list becomes:
310+
```
311+
0 → [1]
312+
1 → [2]
313+
3 → [1, 2]
314+
```
315+
316+
317+
318+
```cpp
319+
cout << "Topological sorting of the graph: ";
320+
topologicalSort(adj, V);
321+
return 0;
322+
}
323+
```
324+
325+
- **`cout << ...`**:
326+
- Prints the message before calling the `topologicalSort` function.
327+
328+
- **`topologicalSort(adj, V);`**:
329+
- Computes the topological order and prints it.
330+
331+
332+
333+
### **Example Execution**
334+
335+
#### Input:
336+
```
337+
V = 4
338+
Edges: {{0, 1}, {1, 2}, {3, 1}, {3, 2}}
339+
```
340+
341+
#### Adjacency List:
342+
```
343+
0 → [1]
344+
1 → [2]
345+
3 → [1, 2]
346+
```
347+
348+
#### Steps:
349+
1. Start DFS from `0`:
350+
- Visit `0 → 1 → 2`.
351+
- Push `2`, then `1`, then `0` to the stack.
352+
2. Start DFS from `3`:
353+
- Visit `3 → 1 → 2` (already visited).
354+
- Push `3` to the stack.
355+
356+
#### Stack:
357+
```
358+
Stack: [3, 0, 1, 2]
359+
```
360+
361+
#### Output:
362+
```
363+
Topological sorting of the graph: 3 0 1 2
364+
```
365+
366+
367+
368+
### **Time and Space Complexity**
369+
370+
1. **Time Complexity**:
371+
- Visiting all nodes: **O(V)**.
372+
- Traversing all edges: **O(E)**.
373+
- Total: **O(V + E)**.
374+
375+
2. **Space Complexity**:
376+
- Stack and visited array: **O(V)**.
377+
- Total: **O(V)**.

0 commit comments

Comments
 (0)