Cycle Detection in Undirected Graph in DSA C# - Time & Space Complexity
We want to understand how long it takes to check if an undirected graph has a cycle.
The question is: how does the time grow as the graph gets bigger?
Analyze the time complexity of the following code snippet.
// Detect cycle in undirected graph using DFS
bool DFS(int node, int parent, List<int>[] graph, bool[] visited) {
visited[node] = true;
foreach (int neighbor in graph[node]) {
if (!visited[neighbor]) {
if (DFS(neighbor, node, graph, visited)) return true;
} else if (neighbor != parent) {
return true; // cycle found
}
}
return false;
}
bool HasCycle(List<int>[] graph) {
int n = graph.Length;
bool[] visited = new bool[n];
for (int i = 0; i < n; i++) {
if (!visited[i]) {
if (DFS(i, -1, graph, visited)) return true;
}
}
return false;
}
This code checks if an undirected graph contains a cycle by visiting nodes using depth-first search.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Visiting each node and its neighbors once using DFS recursion.
- How many times: Each edge and node is checked once during the DFS traversal.
As the graph grows, the time to check all nodes and edges grows roughly in proportion to the number of nodes plus edges.
| Input Size (n nodes, e edges) | Approx. Operations |
|---|---|
| 10 nodes, 15 edges | About 25 operations (10 + 15) |
| 100 nodes, 200 edges | About 300 operations (100 + 200) |
| 1000 nodes, 3000 edges | About 4000 operations (1000 + 3000) |
Pattern observation: The operations grow 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 DFS will check each edge multiple times, so the time is much higher than linear."
[OK] Correct: Each edge is visited only twice (once from each node), so the total work is proportional to edges plus nodes, not more.
Understanding this time complexity helps you explain how graph traversal scales and why DFS is efficient for cycle detection.
"What if the graph was directed instead of undirected? How would the time complexity change?"
Practice
Solution
Step 1: Understand DFS traversal in undirected graph
DFS visits nodes and keeps track of the parent node to avoid false cycle detection.Step 2: Detect cycle condition
If during DFS, a visited node is found that is not the parent of the current node, it means a cycle exists.Final Answer:
If we visit a node that is already visited and is not the parent, a cycle exists. -> Option AQuick Check:
Cycle detection in undirected graph = check visited node not parent [OK]
- Ignoring parent node in cycle check
- Assuming disconnected graph always has cycle
- Confusing cycle detection with edge count
Solution
Step 1: Understand adjacency list initialization
We need an array of lists, each list initialized separately to hold neighbors.Step 2: Check code correctness
var graph = new List<int>[3]; for(int i=0; i<3; i++) graph[i] = new List<int>(); creates an array of List<int> and initializes each element with a new List<int>, which is correct.Final Answer:
var graph = new List<int>[3]; for(int i=0; i<3; i++) graph[i] = new List<int>(); -> Option AQuick Check:
Initialize array of lists with loop [OK]
- Not initializing each list element
- Using wrong data types for adjacency list
- Assigning list directly to array variable
bool DFS(int node, int parent, List<int>[] graph, bool[] visited) {
visited[node] = true;
foreach(var neighbor in graph[node]) {
if(!visited[neighbor]) {
if(DFS(neighbor, node, graph, visited)) return true;
} else if(neighbor != parent) {
return true;
}
}
return false;
}
// Graph edges: 0-1, 1-2, 2-0
// Call: DFS(0, -1, graph, visited)
What will be the output of this DFS call?Solution
Step 1: Analyze graph structure and DFS call
The graph has edges 0-1, 1-2, and 2-0 forming a triangle cycle. DFS starts at node 0 with parent -1.Step 2: Trace DFS for cycle detection
DFS visits 0, then 1, then 2. When 2 checks neighbors, it finds 0 already visited and not parent, so returns true indicating cycle.Final Answer:
true -> Option DQuick Check:
Triangle graph cycle detected = true [OK]
- Ignoring parent check causing false cycle
- Assuming no cycle in triangle graph
- Confusing return values in recursion
bool DFS(int node, int parent, List<int>[] graph, bool[] visited) {
visited[node] = true;
foreach(var neighbor in graph[node]) {
if(!visited[neighbor]) {
if(DFS(neighbor, node, graph, visited)) return true;
} else {
return true;
}
}
return false;
}
What is the bug in this code?Solution
Step 1: Review cycle detection condition
The code returns true if a neighbor is visited, but does not check if neighbor is parent.Step 2: Identify why this causes incorrect cycle detection
In undirected graphs, the parent node is always visited, so returning true here falsely detects a cycle.Final Answer:
It returns true even if the visited neighbor is the parent node. -> Option BQuick Check:
Must exclude parent node in cycle check [OK]
- Missing parent check in visited condition
- Assuming any visited neighbor means cycle
- Not marking nodes visited before recursion
0: [1, 2] 1: [0, 3] 2: [0, 3] 3: [1, 2, 4] 4: [3]Using DFS cycle detection, which node will first detect the cycle and why?
Solution
Step 1: Understand graph structure and DFS traversal
Assuming neighbor lists are processed in listed order, DFS starts at node 0, visits 1 (first neighbor), then 3 (from 1), then 2 (from 3).Step 2: Identify where cycle is detected
Node 2 has neighbors 0 and 3; 0 is already visited and not parent (parent=3), so cycle is detected at node 2 first.Final Answer:
Node 2, because it connects back to already visited node 0. -> Option CQuick Check:
Cycle detected at node with visited neighbors not parent [OK]
- Assuming cycle detected at start node
- Ignoring multiple visited neighbors
- Confusing leaf nodes with cycle detection
Ready to prove it?
Pass the test and this pattern moves unknown
