Cycle Detection in Undirected Graph in DSA C++ - Time & Space Complexity
We want to understand how the time needed to detect a cycle in an undirected graph changes as the graph grows.
Specifically, how does the number of nodes and edges affect the work done?
Analyze the time complexity of the following code snippet.
bool dfs(int node, int parent, vector<vector<int>>& adj, vector<bool>& visited) {
visited[node] = true;
for (int neighbor : adj[node]) {
if (!visited[neighbor]) {
if (dfs(neighbor, node, adj, visited)) return true;
} else if (neighbor != parent) {
return true;
}
}
return false;
}
bool hasCycle(int n, vector<vector<int>>& adj) {
vector<bool> visited(n, false);
for (int i = 0; i < n; i++) {
if (!visited[i]) {
if (dfs(i, -1, adj, visited)) return true;
}
}
return false;
}
This code checks if an undirected graph has a cycle by visiting nodes using depth-first search.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Visiting each node and exploring its neighbors using DFS recursion.
- How many times: Each node and each edge is visited once during the DFS traversal.
As the number of nodes (n) and edges (e) grow, the DFS explores all nodes and edges once.
| Input Size (n, e) | Approx. Operations |
|---|---|
| 10 nodes, 15 edges | About 40 visits (nodes + edges) |
| 100 nodes, 200 edges | About 500 visits |
| 1000 nodes, 3000 edges | About 4000 visits |
Pattern observation: The work grows roughly in proportion to the sum of nodes and edges.
Time Complexity: O(n + e)
This means the time to detect a cycle grows linearly with the total number of nodes plus edges in the graph.
[X] Wrong: "The DFS visits every edge multiple times, so the time is much higher than O(n + e)."
[OK] Correct: Each edge is checked only twice (once from each node), so the total work is still proportional to n + e, not more.
Understanding this time complexity helps you explain how graph traversal algorithms scale 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 tracks the parent node to avoid false cycle detection.Step 2: Detect cycle condition
If a visited node is found that is not the parent, it means a back edge exists, indicating a cycle.Final Answer:
If we visit a node that is already visited and is not the parent, a cycle exists. -> Option BQuick Check:
Cycle detection in undirected graph = check visited and parent [OK]
- Ignoring the parent node in cycle check
- Assuming disconnected graph always has cycles
- Confusing cycle detection with number of edges
Solution
Step 1: Analyze function parameters for DFS cycle detection
We need current node, parent node, adjacency list (graph), and visited array.Step 2: Check return type and parameter types
Return type should be bool to indicate cycle presence; graph is vector of vectors; visited is bool vector.Final Answer:
bool dfs(int node, int parent, vector& graph, vector<bool>& visited); -> Option AQuick Check:
Correct DFS signature includes parent and returns bool [OK]
- Using wrong graph data type (vector<int> instead of vector)
- Missing parent parameter
- Wrong return type for cycle detection
int main() {
vector graph = {{1,2}, {0,2}, {0,1,3}, {2}};
vector<bool> visited(4, false);
bool hasCycle = false;
function dfs = [&](int node, int parent) {
visited[node] = true;
for (int adj : graph[node]) {
if (!visited[adj]) {
if (dfs(adj, node)) return true;
} else if (adj != parent) {
return true;
}
}
return false;
};
for (int i = 0; i < 4; i++) {
if (!visited[i]) {
if (dfs(i, -1)) {
hasCycle = true;
break;
}
}
}
cout << (hasCycle ? "Cycle Found" : "No Cycle") << endl;
return 0;
}Solution
Step 1: Analyze graph structure
Graph edges: 0-1, 0-2, 1-2, 2-3. Nodes 0,1,2 form a triangle (cycle).Step 2: Trace DFS cycle detection
DFS visits nodes and finds back edge between 0 and 2 or 1 and 2, detecting cycle.Final Answer:
Cycle Found -> Option DQuick Check:
Triangle in graph means cycle detected [OK]
- Ignoring back edge condition
- Assuming no cycle if graph is connected
- Confusing parent node with visited node
bool dfs(int node, int parent, vector& graph, vector<bool>& visited) {
visited[node] = true;
for (int adj : graph[node]) {
if (!visited[adj]) {
if (dfs(adj, node, graph, visited)) return true;
} else {
return true;
}
}
return false;
}Solution
Step 1: Review cycle detection condition
The else block returns true if adjacent node is visited, but does not check if it is the parent.Step 2: Identify impact of missing parent check
Returning true for parent node causes false positive cycle detection.Final Answer:
It returns true even if adjacent node is the parent, causing false cycle detection. -> Option AQuick Check:
Check parent node before returning true for visited adjacency [OK]
- Not checking if visited node is parent
- Returning true too early
- Incorrect recursion parameters
Solution
Step 1: Understand cycle detection with node tracking
DFS can track the current path using a stack or vector to record nodes visited in the current recursion.Step 2: Modify DFS to return cycle path
When a back edge is found (visited node not parent), extract nodes from the path stack to get cycle nodes.Final Answer:
Modify DFS to store the path stack and return the cycle path when a back edge is found. -> Option CQuick Check:
Track path in DFS to identify cycle nodes [OK]
- Using BFS which doesn't track path easily
- Ignoring parent check causing false cycles
- Using edge count alone to identify cycle nodes
Ready to prove it?
Pass the test and this pattern moves unknown
