Topological Sort Using Kahn's Algorithm BFS in DSA Rust - Time & Space Complexity
We want to understand how the time needed to perform topological sort using Kahn's Algorithm grows as the graph size increases.
Specifically, how the number of nodes and edges affects the work done.
Analyze the time complexity of the following code snippet.
fn kahn_topological_sort(graph: &Vec>, indegree: &mut Vec) -> Vec {
let mut queue = std::collections::VecDeque::new();
for i in 0..graph.len() {
if indegree[i] == 0 {
queue.push_back(i);
}
}
let mut topo_order = Vec::new();
while let Some(node) = queue.pop_front() {
topo_order.push(node);
for &neighbor in &graph[node] {
indegree[neighbor] -= 1;
if indegree[neighbor] == 0 {
queue.push_back(neighbor);
}
}
}
topo_order
}
This code performs a topological sort on a directed graph using Kahn's Algorithm with BFS.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Visiting each node and its edges once.
- How many times: Each node is enqueued and dequeued once; each edge is checked once.
As the number of nodes (n) and edges (e) grow, the algorithm processes each node and edge 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 in direct proportion to the sum of nodes and edges.
Time Complexity: O(n + e)
This means the time grows linearly with the number of nodes plus the number of edges in the graph.
[X] Wrong: "The algorithm takes O(n²) time because it uses nested loops."
[OK] Correct: Although there are loops inside loops, each edge and node is processed only once, so the total work is linear, not quadratic.
Understanding this time complexity helps you explain how efficient graph algorithms work and shows you can analyze BFS-based approaches clearly.
"What if the graph is represented using an adjacency matrix instead of adjacency lists? How would the time complexity change?"
Practice
Solution
Step 1: Understand Kahn's Algorithm goal
Kahn's Algorithm is designed to produce a linear ordering of vertices in a Directed Acyclic Graph (DAG) such that for every directed edge uv, vertex u comes before v in the ordering.Step 2: Compare with other graph algorithms
Shortest path algorithms find paths, cycle detection checks for loops, and minimum spanning tree algorithms work on undirected graphs. Kahn's Algorithm specifically produces a topological order.Final Answer:
To find a topological ordering of a Directed Acyclic Graph (DAG) -> Option AQuick Check:
Topological sort = Kahn's Algorithm purpose [OK]
- Confusing topological sort with shortest path
- Thinking it detects cycles in undirected graphs
- Mixing it with minimum spanning tree algorithms
Solution
Step 1: Identify the need for queue behavior
Kahn's Algorithm processes nodes with zero incoming edges in FIFO order, so a queue is needed to efficiently pop from front and push at back.Step 2: Choose the Rust data structure
VecDeque provides efficient push and pop operations at both ends, making it ideal for queue operations. HashMap is for key-value storage, BinaryHeap is a priority queue, and LinkedList is less efficient in Rust.Final Answer:
VecDeque (double-ended queue) -> Option BQuick Check:
Queue for zero in-degree nodes = VecDeque [OK]
- Using HashMap instead of a queue
- Choosing BinaryHeap which is a priority queue
- Using LinkedList which is inefficient in Rust
let edges = vec![(0, 1), (0, 2), (1, 3), (2, 3)];
let n = 4;
// Assume graph and in_degree are built correctly
// After running Kahn's Algorithm, the order vector is printed
println!("{:?}", order);Solution
Step 1: Analyze the graph edges and in-degree
Edges: 0->1, 0->2, 1->3, 2->3. Node 0 has in-degree 0, nodes 1 and 2 have in-degree 1, node 3 has in-degree 2.Step 2: Perform Kahn's Algorithm BFS
Start with node 0 (in-degree 0). Remove it, reduce in-degree of 1 and 2 to 0. Add 1 and 2 to queue. Next, remove 1, then 2, then 3. Order is [0, 1, 2, 3]. However, since nodes 1 and 2 become zero in-degree simultaneously, the order of processing them can vary. If we process 2 before 1, the order is [0, 2, 1, 3], which is also valid.Final Answer:
[0, 2, 1, 3] -> Option CQuick Check:
Topological order respects edges = [0, 2, 1, 3] [OK]
- Reversing order of nodes processed
- Ignoring in-degree updates
- Assuming multiple valid orders without checking edges
for &node in &graph[node] {
in_degree[node] -= 1;
if in_degree[node] == 0 {
queue.push_back(node);
}
}Solution
Step 1: Understand variable shadowing in the loop
The loop uses 'node' as the loop variable, which shadows the outer 'node' variable used to index graph. This causes confusion and incorrect indexing.Step 2: Correct the variable naming
Use a different variable name like 'neighbor' in the loop to avoid shadowing and correctly access neighbors.Final Answer:
Using 'node' as loop variable shadows outer 'node', causing wrong indexing -> Option DQuick Check:
Variable shadowing causes bugs = Using 'node' as loop variable shadows outer 'node', causing wrong indexing [OK]
- Ignoring variable shadowing
- Confusing queue with stack usage
- Assuming in_degree initialization is the bug
Solution
Step 1: Calculate in-degree for each node
Nodes: 0,1,2,3,4,5 In-degree: 0(2), 1(2), 2(1), 3(1), 4(0), 5(0)Step 2: Perform Kahn's Algorithm steps
Start with nodes 4 and 5 (in-degree 0). Remove 5: reduce in-degree of 2 and 0. Remove 4: reduce in-degree of 0 and 1. Then remove 2, 3, 1, 0 in order. One valid order is [5, 4, 2, 3, 1, 0].Final Answer:
[5, 4, 2, 3, 1, 0] -> Option AQuick Check:
Valid topological order respects edges = [5, 4, 2, 3, 1, 0] [OK]
- Choosing orders that violate edge directions
- Ignoring initial zero in-degree nodes
- Mixing node order without updating in-degree
Ready to prove it?
Pass the test and this pattern moves unknown
