Cycle Detection in Directed Graph in DSA C# - Time & Space Complexity
We want to understand how long it takes to find a cycle in a directed graph.
Specifically, how the time grows as the graph gets bigger.
Analyze the time complexity of the following code snippet.
// Detect cycle using DFS in directed graph
bool HasCycle(int node, List<int>[] graph, bool[] visited, bool[] recStack) {
if (!visited[node]) {
visited[node] = true;
recStack[node] = true;
foreach (int neighbor in graph[node]) {
if (!visited[neighbor] && HasCycle(neighbor, graph, visited, recStack))
return true;
else if (recStack[neighbor])
return true;
}
}
recStack[node] = false;
return false;
}
This code checks if there is a cycle starting from a given node using depth-first search.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive depth-first search visiting each node and its neighbors.
- How many times: Each node is visited once, and each edge is checked once during recursion.
As the number of nodes and edges grows, the work grows roughly in proportion to their total.
| Input Size (n nodes, e edges) | Approx. Operations |
|---|---|
| 10 nodes, 15 edges | About 25 checks (nodes + edges) |
| 100 nodes, 200 edges | About 300 checks |
| 1000 nodes, 5000 edges | About 6000 checks |
Pattern observation: The operations grow roughly linearly with the sum of nodes and edges.
Time Complexity: O(n + e)
This means the time grows linearly with the number of nodes plus edges in the graph.
[X] Wrong: "The algorithm checks every possible path, so it must be exponential time."
[OK] Correct: The algorithm marks nodes visited and does not revisit them, so it only processes each node and edge once, avoiding repeated work.
Understanding this time complexity helps you explain how your cycle detection scales and why it is efficient for large graphs.
"What if the graph was represented as an adjacency matrix instead of adjacency lists? How would the time complexity change?"
Practice
Solution
Step 1: Understand cycle in directed graph
A cycle means a path that starts and ends at the same vertex following the direction of edges.Step 2: Identify what cycle detection finds
Cycle detection checks if such a path exists in the graph.Final Answer:
Whether there is a path that starts and ends at the same vertex following edge directions -> Option AQuick Check:
Cycle detection = finding directed cycles [OK]
- Confusing cycle with shortest path
- Ignoring edge directions
- Thinking cycle means any repeated vertex
Solution
Step 1: Recall DFS cycle detection method
During DFS, we track nodes in the current recursion path to detect back edges.Step 2: Identify data structure used
This is done using a stack or a boolean array marking nodes in the recursion stack.Final Answer:
Stack or boolean array to mark recursion stack -> Option BQuick Check:
Recursion stack tracking = Stack/boolean array [OK]
- Using queue which is FIFO, not suitable for recursion stack
- Confusing dictionary with recursion tracking
- Not marking nodes currently in recursion
var graph = new Dictionary<int, List<int>> {
{1, new List<int>{2}},
{2, new List<int>{3}},
{3, new List<int>{1}}
};
bool HasCycle(int node, HashSet<int> visited, HashSet<int> recStack) {
if (!visited.Contains(node)) {
visited.Add(node);
recStack.Add(node);
foreach (var neighbor in graph[node]) {
if (!visited.Contains(neighbor) && HasCycle(neighbor, visited, recStack))
return true;
else if (recStack.Contains(neighbor))
return true;
}
}
recStack.Remove(node);
return false;
}
var visited = new HashSet<int>();
var recStack = new HashSet<int>();
Console.WriteLine(HasCycle(1, visited, recStack));Solution
Step 1: Analyze graph edges
The graph has edges 1->2, 2->3, and 3->1 forming a cycle.Step 2: Trace DFS cycle detection
Starting at node 1, recursion visits 2, then 3, then back to 1 which is in recursion stack, so cycle detected.Final Answer:
True -> Option DQuick Check:
Cycle exists in graph = True [OK]
- Ignoring recursion stack check
- Assuming no cycle because graph looks simple
- Confusing visited set with recursion stack
bool HasCycle(int node, HashSet<int> visited, HashSet<int> recStack) {
visited.Add(node);
foreach (var neighbor in graph[node]) {
if (!visited.Contains(neighbor) && HasCycle(neighbor, visited, recStack))
return true;
else if (recStack.Contains(neighbor))
return true;
}
recStack.Remove(node);
return false;
}Solution
Step 1: Check recursion stack usage
Node must be added to recStack before exploring neighbors to detect back edges.Step 2: Identify missing step
Code adds node to visited but never adds to recStack before recursion, so cycle detection fails.Final Answer:
Missing adding node to recStack before recursion -> Option CQuick Check:
recStack update missing = bug [OK]
- Forgetting to add node to recursion stack
- Removing node from visited incorrectly
- Confusing visited and recursion stack roles
var graph = new Dictionary<int, List<int>> {
{1, new List<int>{2, 3}},
{2, new List<int>{4}},
{3, new List<int>{4}},
{4, new List<int>{5}},
{5, new List<int>{3}}
};
Which nodes form a cycle in this graph?Solution
Step 1: Trace edges to find cycle
Edges: 3->4, 4->5, 5->3 form a cycle among nodes 3, 4, 5.Step 2: Confirm cycle presence
Starting at 3, path 3->4->5->3 loops back, confirming cycle.Final Answer:
Nodes 3, 4, 5 -> Option AQuick Check:
Cycle nodes = 3,4,5 [OK]
- Ignoring edge 5->3 that closes cycle
- Assuming no cycle because 1 and 2 are acyclic
- Confusing cycle nodes with all connected nodes
Ready to prove it?
Pass the test and this pattern moves unknown
