Detect Cycle in Undirected Graph (BFS)
Does an undirected graph contain a cycle? Detect it with BFS.
Open on GeeksforGeeks ↗Intuition
In an undirected graph every edge can be walked back the way you came, so 'neighbour already visited' is not enough to prove a cycle — it might just be your parent. Carry the parent along in the queue: an already-visited neighbour that is not the parent means a second, independent path reached it, which closes a loop.
The BFS counterpart to the DFS parent trick: carry each vertex's parent through the queue, and any already-seen neighbour that isn't the parent is a back edge. Storing (vertex, parent) pairs is the whole adaptation — BFS has no call stack to inspect, so the provenance rides along with the data.
Approach
The parent exception
Queue (vertex, parent) pairs. When examining a neighbour that is already visited, ignore it if it is the parent — that is only the edge you arrived on.
Any other visited neighbour is a cycle
If a visited neighbour is not the parent, two distinct paths reach it, so the graph has a cycle. Return immediately.
Repeat per component
A cycle may hide in any component, so restart BFS at each unvisited vertex.
Solution & live demo
Common pitfalls
Queueing vertices without their parent
q = deque([s]) ... elif seen[u]: return True
q = deque([(s, -1)]) ... elif u != parent: return True
Every undirected edge appears in both adjacency lists, so the vertex you came from is always already visited — without the parent you report a cycle on a single edge. BFS can't inspect the call stack, so the parent must travel in the queue.
Marking visited at dequeue time
v, parent = q.popleft() seen[v] = True
if not seen[u]:
seen[u] = True
q.append((u, v))Two vertices can enqueue the same neighbour before either dequeues it, and the second arrival then looks like a back edge — a false cycle on a perfectly acyclic tree. Marking at enqueue time makes each vertex enter exactly once.
Excluding the parent by identity rather than per-edge
elif u != parent: return True # with parallel edges present
elif u != parent: return True # correct for simple graphs
Worth knowing the limit: this test assumes no duplicate edges between the same pair. Two parallel edges are a cycle, but both look like the parent and get excused. For multigraphs you must track edge identity, not vertex identity.
Edge cases
Never finds a non-parent visited neighbour — no cycle.
Each component is checked separately.
The second edge looks like a non-parent revisit and is correctly reported as a cycle.