Detect Cycle in an Undirected Graph
WHAT IT SAYS
Determine whether an undirected graph contains any cycle.
WHAT IT'S REALLY ASKING
"A traversal exploring fresh territory never meets a visited node — unless two different routes lead to the same place, and two routes to one place IS a cycle. One nuance: in an undirected graph every edge points both ways, so the node you just came from always looks 'visited'. The signal is meeting a visited node that is NOT the one you arrived from."
Hunt for a path that returns to its start
O(V · (V + E)) — a full search per candidate start, with path bookkeepingFor each vertex, launch a DFS that tracks the current path, looking for any walk that leaves the vertex and comes back to it without reusing an edge. If any vertex admits such a round trip, report a cycle.
WHERE THE WORK IS WASTED — Cycles are being hunted per-starting-point, but a cycle through vertices a-b-c is discovered redundantly from a, from b, AND from c — the same structure certified three times by three searches. Deeper: the definition being chased ('a walk returning to start') is needlessly operational. A single traversal already generates enough evidence to convict: the moment ANY two distinct routes converge on one node, a cycle exists somewhere behind them, no round trip required. The per-start hunt rebuilds globally what one sweep observes locally.
Convergence convicts — except convergence with your own parent.
The clean claim: an undirected graph has a cycle if and only if, during a traversal, some node is reached that was ALREADY visited via a different route. Why: two distinct routes from the traversal's origin to one node form, together, a closed loop (walk out on route one, back on route two — distinctness guarantees some edge isn't retraced). Conversely, in an acyclic graph (a forest), there is exactly ONE simple path between any two nodes, so a traversal can never arrive anywhere twice by genuinely different routes. Now the nuance that separates this from the directed version, and it comes straight from representation: an undirected edge u-v is stored as two directed arcs, u→v and v→u. So the DFS standing at v (having arrived from u) sees u in its neighbour list — visited! — but this is not convergence of two routes; it is the SAME edge read backwards. The fix is one extra parameter: carry the parent (the node you arrived from), and exempt it from suspicion. The verdict becomes: a visited neighbour OTHER than my parent means a genuine second route — cycle. A visited neighbour that IS my parent is the echo of my own arrival — ignore. Be precise about what the parent-exemption assumes: simple graphs. Parallel edges (two distinct u-v edges) or self-loops ARE cycles, yet the naive exemption waves them through; if the input allows multi-edges, exempt the parent EDGE (by index), not the parent node. The graph may be disconnected, so sweep all vertices and launch from each unvisited one — the launches partition the graph, each searching one component, and any component may hold the cycle. The union-find alternative deserves its own sentence because its logic is pleasingly inverted: process edges one by one, uniting endpoints; an edge whose endpoints are ALREADY in the same set is announcing that a path between them existed before this edge — adding it closes a loop. Detection at insertion time, no traversal at all — the same 'second route' logic, restated as 'second connection'.
DFS with a parent parameter, launched per component
O(V + E) time for traversal or union-find (near-linear), O(V) spaceFor each unvisited vertex, DFS(v, parent = -1): mark v; for each neighbour u — if unvisited, recurse DFS(u, v) and propagate a found-cycle; else if u ≠ parent, return true. BFS variant: queue holds (node, parent) pairs, same exemption. Union-find variant: for each edge, if find(u) == find(v) return true, else union.
Second Route Means a Loop
YOU'LL SEE IT AGAIN WHEN
- The question is existence of a cycle in an UNDIRECTED graph — so any revisit via a distinct route convicts, no path-stack needed.
- Each edge is stored twice, so the arrival edge echoes as a visited neighbour — the parent exemption is mandatory, and it assumes no multi-edges.
- Edges arrive incrementally or a spanning structure is being built — union-find's same-set test detects the loop at insertion.