Topological Sort Using DFS in DSA Go - Time & Space Complexity
We want to understand how the time needed to perform topological sort using DFS changes as the graph size grows.
Specifically, how the number of nodes and edges affects the work done.
Analyze the time complexity of the following code snippet.
func topoSortDFS(graph map[int][]int, node int, visited map[int]bool, stack *[]int) {
visited[node] = true
for _, neighbor := range graph[node] {
if !visited[neighbor] {
topoSortDFS(graph, neighbor, visited, stack)
}
}
*stack = append(*stack, node)
}
func TopologicalSort(graph map[int][]int) []int {
visited := make(map[int]bool)
stack := []int{}
for node := range graph {
if !visited[node] {
topoSortDFS(graph, node, visited, &stack)
}
}
// reverse stack for correct order
for i, j := 0, len(stack)-1; i < j; i, j = i+1, j-1 {
stack[i], stack[j] = stack[j], stack[i]
}
return stack
}
This code performs a topological sort on a directed graph using depth-first search (DFS).
Identify the loops, recursion, array traversals that repeat.
- Primary operation: DFS visits each node and explores all its edges once.
- How many times: Each node is visited once, and each edge is explored once during recursion.
As the number of nodes (n) and edges (e) increase, the DFS explores all nodes and edges once.
| Input Size (n, e) | Approx. Operations |
|---|---|
| 10 nodes, 15 edges | About 25 operations (10 nodes + 15 edges) |
| 100 nodes, 200 edges | About 300 operations (100 + 200) |
| 1000 nodes, 5000 edges | About 6000 operations (1000 + 5000) |
Pattern observation: The work grows roughly proportional to the sum of nodes and edges.
Time Complexity: O(n + e)
This means the time grows linearly with the total number of nodes and edges in the graph.
[X] Wrong: "Topological sort takes O(n²) time because of nested loops."
[OK] Correct: The DFS visits each node and edge only once, so it does not repeatedly check all pairs. The graph structure guides the visits efficiently.
Understanding this time complexity helps you explain how graph algorithms scale and why DFS is efficient for ordering tasks with dependencies.
"What if the graph was represented using an adjacency matrix instead of adjacency lists? How would the time complexity change?"
Practice
Solution
Step 1: Understand DFS role in Topological Sort
DFS visits nodes deeply, ensuring all dependencies of a node are visited before the node itself.Step 2: Relate DFS order to dependencies
By pushing nodes to a stack after visiting all neighbors, we get an order where dependencies come before dependents.Final Answer:
To visit nodes in a way that ensures dependencies come before dependents -> Option AQuick Check:
DFS ensures dependency order = A [OK]
- Confusing DFS with BFS for topological sort
- Thinking topological sort finds shortest paths
- Assuming DFS detects cycles in undirected graphs here
Solution
Step 1: Check visited condition and marking
func dfs(node int, graph map[int][]int, visited map[int]bool, stack *[]int) { if !visited[node] { visited[node] = true for _, neighbor := range graph[node] { dfs(neighbor, graph, visited, stack) } *stack = append(*stack, node) } } correctly checks if node is not visited before processing and marks it visited inside the condition.Step 2: Confirm stack append and recursion
It recursively calls dfs on neighbors and appends node to stack after visiting neighbors, using pointer to modify stack.Final Answer:
Option D code snippet -> Option DQuick Check:
Correct DFS recursion and stack append = D [OK]
- Passing stack by value causing no changes
- Marking visited after recursion causing repeated visits
- Appending node before visiting neighbors
graph := map[int][]int{
5: {2, 0},
4: {0, 1},
2: {3},
3: {1},
0: {},
1: {},
}
Assuming DFS visits nodes in ascending order starting from 5, then 4.Solution
Step 1: Perform DFS starting from node 5
DFS from 5 (neighbors ascending: 0 then 2): DFS(0) append 0; then DFS(2) -> DFS(3) -> DFS(1) append 1; append 3; append 2; append 5. Then DFS from 4 (0,1 visited), append 4.Step 2: Collect nodes in post-order (append after visiting neighbors)
Post-order stack push order: 0, 1, 3, 2, 5, 4. Reversing gives [4, 5, 2, 3, 1, 0].Step 3: Consider DFS starting order and visited nodes
Since DFS visits 5 first, then 4, the final topological order is the reverse of the post-order stack: [4, 5, 2, 3, 1, 0]. However, the question states DFS visits nodes in ascending order starting from 5, then 4. The actual post-order stack is [0,1,3,2,5,4], so reversed order is [4,5,2,3,1,0].Final Answer:
[4, 5, 2, 3, 1, 0] -> Option BQuick Check:
Post-order reverse = [4,5,2,3,1,0] [OK]
- Not reversing the stack after DFS
- Visiting nodes in wrong order
- Ignoring already visited nodes
func dfs(node int, graph map[int][]int, visited map[int]bool, stack *[]int) {
if visited[node] {
return
}
for _, neighbor := range graph[node] {
dfs(neighbor, graph, visited, stack)
}
visited[node] = true
*stack = append(*stack, node)
}
Solution
Step 1: Check visited marking timing
Visited is marked after recursive calls, so neighbors may revisit the same node multiple times.Step 2: Correct visited marking position
Visited should be marked before recursion to avoid repeated visits and infinite loops.Final Answer:
Visited is marked after recursion, causing repeated visits -> Option AQuick Check:
Mark visited before recursion = B [OK]
- Marking visited too late causing infinite recursion
- Appending stack before recursion
- Ignoring initialization of visited map
1 -> 2, 1 -> 3, 3 -> 4, 2 -> 4, 4 -> 5If you want to find a valid order to complete tasks represented by nodes, which topological sort order is correct?
Solution
Step 1: Analyze dependencies from edges
Node 1 must come before 2 and 3; 2 and 3 before 4; 4 before 5.Step 2: Find order respecting dependencies
Order [1, 2, 3, 4, 5] respects all edges: 1 before 2 and 3, 2 and 3 before 4, 4 before 5.Final Answer:
[1, 2, 3, 4, 5] -> Option CQuick Check:
Dependencies respected = C [OK]
- Reversing order ignoring dependencies
- Placing dependent before dependency
- Choosing order with cycles
Ready to prove it?
Pass the test and this pattern moves unknown
