3.5.6 · D4Graphs

Exercises — DFS applications — cycle detection (directed and undirected)

2,238 words10 min readBack to topic

This page is a self-test ladder. Work each problem on paper before opening its solution. Levels climb from "just spot it" to "design the whole thing".

If any word here is unfamiliar (White/Gray/Black, back edge, parent-skip), reread the parent first: the topic note, and lean on Depth-First Search for the traversal engine.


Level 1 — Recognition

Goal: given a tiny graph, say cycle / no cycle and name the edge that proves it.

L1.1

Directed graph with edges , , . Cycle or not? Which colour does every node end in?

Recall Solution

No cycle. It is a straight path — nobody points back. DFS from : paint Gray one after another, hit no Gray neighbour, then finish them back up so all four end Black. A finished (Black) chain with no back edge = acyclic.

L1.2

Undirected graph with edges , , . Cycle or not? Which single edge closes it?

Recall Solution

Cycle. It is a triangle. Walking , from we see neighbour which is visited and not the parent (). The edge closes the loop.

L1.3

Directed self-loop: one node with edge . Cycle?

Recall Solution

Yes — the smallest possible directed cycle. Enter , paint it Gray, scan its neighbours, find itself which is currently Gray → back edge → cycle immediately.


Level 2 — Application

Goal: run the algorithm by hand and produce the correct verdict.

L2.1

Directed: , , , . Run 3-colour DFS from . Cycle?

Recall Solution
  • dfs(A): A=Gray. Try .
  • dfs(B): B=Gray. Try .
  • dfs(C): C=Gray. Neighbour is Gray → back edge cycle = True.

The 2-cycle is the culprit.

L2.2

Undirected: , , , . Run dfs(A,-1). Cycle? Name the closing edge.

Recall Solution
  • dfs(A,-1): visit A → go .
  • dfs(B,A): visit B → skip parent → go .
  • dfs(C,B): visit C → skip parent → go .
  • dfs(D,C): visit D → parent is (skip). Neighbour is visited and parent cycle = True.

Closing edge: . The loop is .

L2.3

Undirected star: centre joined to , , , (four edges, nothing else). Cycle?

Recall Solution

No cycle. From we visit each leaf; each leaf's only neighbour is = its parent = skipped. A star on nodes has edges → it is a tree → acyclic.


Level 3 — Analysis

Goal: reason about why a rule holds, or debug a subtle case.

L3.1

A student uses the undirected parent-trick on the directed graph , . Their code returns "no cycle". Trace their mistaken run and give the correct verdict.

Recall Solution

Their buggy dfs(u,parent) ignores any visited neighbour equal to parent:

  • dfs(A,-1): visit A → go .
  • dfs(B,A): visit B → neighbour is visited and equals parentskipped → returns no cycle.

Wrong. The true graph has the 2-cycle . The parent-skip erased exactly the edge that proves it. Correct verdict: cycle exists. Directed edges are not symmetric, so there is no "spurious reverse copy" to forgive — the edge is a genuine distinct arrow. Use 3 colours instead; sees still Gray → back edge.

L3.2

In the multigraph below (undirected), there are two edges between and and nothing else. Does the plain v != parent algorithm detect the cycle? Should it?

Recall Solution

Two parallel edges are a cycle (go out on edge 1, come back on edge 2). But plain parent-skip fails:

  • dfs(A,-1): visit A → go via edge 1.
  • dfs(B,A): visit B → scan neighbours: (via edge 2) is visited but equals parent → skipped. Missed!

Fix: track the edge index you arrived on, not just the parent vertex. Then edge 2 ( arrival edge) is recognised as a genuine return route → cycle. (See figure below.)

Figure — DFS applications — cycle detection (directed and undirected)

L3.3

Prove: an undirected simple graph with nodes and edges must contain at least one cycle.

Recall Solution

A forest (acyclic undirected graph) with nodes and connected pieces has exactly edges. Since , a forest has at most edges. We have edges , so the graph is not a forest → it contains a cycle. (Equivalently: run DFS; with edges but only tree edges possible, at least one edge is a back edge.)


Level 4 — Synthesis

Goal: combine cycle detection with a neighbouring idea.

L4.1

You must run Topological Sort on a directed graph. Explain precisely how the directed cycle test decides whether a topological order even exists, and what edge type blocks it.

Recall Solution

A topological order exists iff the graph is a DAG (no directed cycle). DFS-based topo sort emits nodes in decreasing finish time. If during that DFS you ever find an edge to a Gray node — a back edge — a directed cycle exists, so no valid ordering exists and topo sort must fail/report the cycle. So the same Gray-check is the acyclicity gatekeeper. See Back Edges and Edge Classification for why only back edges (not forward/cross) signal cycles.

L4.2

Give a graph where DFS-based undirected cycle detection and Union-Find (DSU)-based detection would flag the same cycle, then explain the different "moment of discovery".

Recall Solution

Triangle , , .

  • DFS: discovers the cycle when, deep in recursion at , it sees visited non-parent (edge ).
  • DSU: processes edges one at a time. It unions , then (via ). When it reaches , both endpoints are already in the same set → cycle. Same closing edge , but DSU never traverses depth — it just asks "are these two already connected?" DSU shines when you only have an edge list and no need for traversal order.

L4.3

A single DFS colours a directed graph and finds zero Gray-hits. Someone claims "then the graph is also bipartite." True or false? Justify using the right tool.

Recall Solution

False. No directed cycle just means the graph is a DAG; it says nothing about 2-colourability. Bipartiteness is about odd-length undirected cycles and is tested with Bipartite Checking (2-colour DFS/BFS on the undirected view). A DAG like , , has an odd undirected cycle-free structure here, but in general acyclic-directed and bipartite are unrelated properties. Different question → different tool.


Level 5 — Mastery

Goal: design, prove correctness, and handle every case.

L5.1

Design an algorithm that, in one DFS pass, returns not just whether a directed cycle exists but prints the actual cycle vertices in order. State the invariant and complexity.

Recall Solution

Keep color[] and a parent[] (who called us). Run the standard 3-colour DFS. The moment dfs(u) finds a neighbour v that is Gray, you have the back edge . Reconstruct the cycle by walking parent[] from u back up until you reach v, then append v:

cycle = [u]
x = u
while x != v:
    x = parent[x]
    cycle.append(x)
reverse(cycle)   # now v ... u, plus the edge u->v closes it

Invariant: at any instant the set of Gray nodes forms exactly the current recursion path (a simple chain via parent[]), so walking parent[] from u up to the Gray ancestor v traces a real path, and the back edge closes it into a genuine cycle. Complexity: still for the DFS; the reconstruction walk is , absorbed into the total.

L5.2

Write the complete driver that detects a directed cycle in a possibly disconnected graph with self-loops, and prove it never misses and never false-alarms.

Recall Solution
for u in all_vertices:
    if color[u] == WHITE:
        if dfs(u): return True      # dfs is the 3-colour version
return False

Never misses: every White vertex eventually starts a DFS (the driver loop), and every edge out of a Gray vertex is scanned, so any back edge — including a self-loop caught while is Gray — is seen. Never false-alarms: we only return True on a Gray neighbour; a Gray neighbour is provably still on the stack (an ancestor), so a real directed path exists from it down to the current node, and the edge back to it closes a real cycle. Black neighbours (finished) are skipped, correctly ignoring forward/cross edges. Complexity: time, space.

L5.3

A graph has vertices. It is undirected, connected, and acyclic. How many edges does it have? Now add one edge — exactly how many simple cycles are created?

Recall Solution

Connected + acyclic = a tree, so it has edges. Adding one edge between two already-connected tree nodes creates exactly one simple cycle: the unique tree path between the endpoints, plus the new edge. So: 5 edges, and adding one gives 1 new simple cycle.


Recall Feynman recap: the one sentence that grades L1–L5

"In directed graphs a cycle is a live back edge (neighbour still Gray); in undirected graphs a cycle is a visited non-parent neighbour (track the edge if the graph is a multigraph) — and always loop over every component."

Connections

  • Depth-First Search — the engine every solution above runs on.
  • Topological Sort — L4.1: acyclicity is exactly the precondition.
  • Union-Find (DSU) — L4.2: edge-list alternative for undirected cycles.
  • Back Edges and Edge Classification — why only back edges signal cycles.
  • Strongly Connected Components — finish-time DFS, same machinery.
  • Bipartite Checking — L4.3: a different DFS signal.