Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
Python Program to Find if Undirected Graph contains Cycle using BFS
A cycle in an undirected graph occurs when you can start at a vertex and return to it by following edges without retracing the same edge. We can detect cycles using Breadth-First Search (BFS) by tracking parent nodes during traversal.
How BFS Cycle Detection Works
The algorithm uses a queue to traverse the graph level by level. For each visited vertex, we track its parent. If we encounter a visited vertex that is not the current vertex's parent, we've found a cycle.
Implementation
Helper Functions
First, let's define a function to add edges to our adjacency list representation ?
from collections import deque
def add_edge(adj, u, v):
adj[u].append(v)
adj[v].append(u)
BFS Cycle Detection
The main cycle detection function uses BFS with parent tracking ?
from collections import deque
def add_edge(adj, u, v):
adj[u].append(v)
adj[v].append(u)
def detect_cycle(adj, start, V, visited):
parent = [-1] * V
queue = deque()
visited[start] = True
queue.append(start)
while queue:
current = queue.popleft()
for neighbor in adj[current]:
if not visited[neighbor]:
visited[neighbor] = True
queue.append(neighbor)
parent[neighbor] = current
elif parent[current] != neighbor:
return True
return False
def has_cycle_disconnected(adj, V):
visited = [False] * V
for i in range(V):
if not visited[i] and detect_cycle(adj, i, V, visited):
return True
return False
# Example usage
V = 5
adj = [[] for i in range(V)]
# Add edges to create a cycle: 0-1-2-0
add_edge(adj, 0, 1)
add_edge(adj, 1, 2)
add_edge(adj, 2, 0)
add_edge(adj, 2, 3)
print("Graph edges:")
print("0 -- 1")
print("1 -- 2")
print("2 -- 0")
print("2 -- 3")
if has_cycle_disconnected(adj, V):
print("\nCycle detected: Yes")
else:
print("\nCycle detected: No")
The output of the above code is ?
Graph edges: 0 -- 1 1 -- 2 2 -- 0 2 -- 3 Cycle detected: Yes
Algorithm Steps
The BFS cycle detection follows these key steps:
- Initialize: Create a parent array and mark all vertices as unvisited
- BFS Traversal: Use a queue to visit vertices level by level
- Parent Tracking: For each vertex, record its parent in the BFS tree
- Cycle Check: If we reach a visited vertex that isn't the current vertex's parent, a cycle exists
- Disconnected Components: Repeat for all unvisited vertices to handle disconnected graphs
Example Without Cycle
Let's test with a graph that has no cycles ?
from collections import deque
def add_edge(adj, u, v):
adj[u].append(v)
adj[v].append(u)
def detect_cycle(adj, start, V, visited):
parent = [-1] * V
queue = deque()
visited[start] = True
queue.append(start)
while queue:
current = queue.popleft()
for neighbor in adj[current]:
if not visited[neighbor]:
visited[neighbor] = True
queue.append(neighbor)
parent[neighbor] = current
elif parent[current] != neighbor:
return True
return False
def has_cycle_disconnected(adj, V):
visited = [False] * V
for i in range(V):
if not visited[i] and detect_cycle(adj, i, V, visited):
return True
return False
# Tree structure (no cycles)
V = 4
adj = [[] for i in range(V)]
add_edge(adj, 0, 1)
add_edge(adj, 1, 2)
add_edge(adj, 1, 3)
print("Tree edges:")
print("0 -- 1")
print("1 -- 2")
print("1 -- 3")
if has_cycle_disconnected(adj, V):
print("\nCycle detected: Yes")
else:
print("\nCycle detected: No")
The output of the above code is ?
Tree edges: 0 -- 1 1 -- 2 1 -- 3 Cycle detected: No
Time and Space Complexity
Time Complexity: O(V + E) where V is vertices and E is edges
Space Complexity: O(V) for the visited array, parent array, and queue
Conclusion
BFS cycle detection works by tracking parent relationships during graph traversal. If we encounter a visited vertex that isn't our parent, we've found a cycle. This approach efficiently handles both connected and disconnected graphs.
