2Brute2Best
🔥0

Detect Cycle in an Undirected Graph

Medium·GeeksforGeeksBasic Anchor
GeeksforGeeks
30-Second Mental ModelUnion-Find (cycle detection, undirected)
Complexity:O(E · (V + E))→O(E · α(V))
Key Insight

Read the edges in list order and keep the vertices in groups with Union-Find. If an edge joins two vertices that are already in one group, that edge closes a cycle, so return true. Every other edge just merges two groups.

Core Validity Invariant

Two vertices with the same root are already joined by a path of edges read so far, so an edge between them closes a cycle.

01

Problem

You get an undirected graph with V vertices, numbered 0 to V − 1, and a list of E edges. Each entry edges[i] = [u, v] is one edge joining vertices u and v. Each edge is listed once.

Return true if the graph contains a cycle, and false if it does not. The graph may have several separate pieces, and some of them may not touch vertex 0.

Input

V, the number of vertices, and edges, the list of edges.

Output

true if the graph has a cycle, otherwise false.

Example 1
Input: V = 4, edges = [[0, 1], [0, 2], [1, 2], [2, 3]]
Output: true

Vertices 0, 1 and 2 form a triangle: 0 → 1 → 2 → 0 comes back to the start.

Example 2
Input: V = 4, edges = [[0, 1], [1, 2], [2, 3]]
Output: false

The edges form one path, and a path has no cycle.

Constraints
  • • 1 ≤ V, E ≤ 10^5
  • • 0 ≤ edges[i][0], edges[i][1] < V
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    A cycle means I can leave a vertex and come back to it without reusing an edge. How can I tell whether two vertices are already joined by a path?

  2. 2.

    If I add the edges one at a time, when does a new edge create a cycle?

  3. 3.

    Can an edge that joins two different pieces ever close a loop?

  4. 4.

    If the graph has at least V edges, must it contain a cycle? What can I say about a graph with fewer edges?

  5. 5.

    Do I need to search from every vertex, or can I decide while reading the list?

03

Brute force approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceRemove one edge; if its ends are still joined, it was on a cycleO(E · (V + E))O(V + E)
OptimalUnion the edges in list order; the first edge whose ends already share a root closes a cycleO(E · α(V))O(V)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “undirected graph”Edges have no arrows, so an edge only links two groups and direction does not matter.
  • “contains a cycle”A cycle is an edge whose two ends are already linked, so find the first such edge.
  • “a list of E edges”Each edge is read once, in list order, so one pass decides the answer.
Edges merge groups of vertices+an edge inside one group = a cycle+answer true at the first such edge→Union-Find (cycle detection, undirected)

The question is about groups, not about how far apart two vertices are, so Union-Find fits. The first edge whose two ends already share a root is a cycle. This is the basic cycle problem: Redundant Connection and Redundant Connection II use the same unite call. Direction changes the answer, though. In 0→1, 0→2, 1→2 there is no cycle, but Union-Find sees 1 and 2 already joined and reports one. Directed graphs need Topological Sort instead.

10

Don't make this mistake

Using the directed 'visited means cycle' rule
✗ bool dfs(int u) { seen[u] = true; for (int v : g[u]) { if (seen[v]) return true; // visited neighbour = cycle? if (dfs(v)) return true; } return false; }
✓ bool dfs(int u, int parent) { seen[u] = true; for (int v : g[u]) { if (v == parent) continue; // the edge we came in by is not a cycle if (seen[v]) return true; if (dfs(v, u)) return true; } return false; }

On the single edge [[0, 1]] with V = 2, dfs(0) visits 1, and then 1 sees 0 already visited, so the wrong code says true. But one edge is a tree. In an undirected graph the edge you came in by always looks visited, so you must skip the parent. The plain visited rule belongs to directed graphs.

Counting edges instead of joining groups
✗ return (int)edges.size() >= V; // E >= V: more edges than a tree allows
✓ if (!dsu.unite(e[0], e[1])) return true; // a cycle is a failed union

A forest on V vertices has at most V − 1 edges, so E ≥ V does prove a cycle. But E < V proves nothing when the graph has several pieces. Triangle 0–1–2 plus the lone vertices 3 and 4 has V = 5 and E = 3, and it still has a cycle, so the count wrongly says false.

Uniting before checking the roots
✗ dsu.unite(e[0], e[1]); if (dsu.find(e[0]) == dsu.find(e[1])) return true;
✓ if (!dsu.unite(e[0], e[1])) return true; // unite reports the failure itself

After a successful unite, the two ends always share a root, so the check fires on the very first edge. Even the single edge [[0, 1]], which has no cycle, returns true. Ask whether the ends are already joined before you merge them.

11

Want to implement this?

Write it yourself on GeeksforGeeks, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Union-Find (DSU) translate into production architectures and low-level memory mechanics.