Skip to content
Work Practice Products Coding interviews Machine learning coding Quant developer HFT course Blog Method Why me About Enquire Book a call

Coding · Graphs

Cycles in a directed graph

Lesson · 9 min read · Graphs · Cycle detection

A visited node is not evidence of a cycle in a directed graph. A node still on the current path is: three colours tell them apart.

Why "visited" is not enough

The undirected test (a visited node other than the parent means a cycle) gives false alarms on a directed graph. Take a diamond: 0 points to 1 and 2, and both point to 3. Searching from 0 through 1 visits 3. Coming back and going through 2, the search meets 3 again, already visited, yet there is no cycle: there is no way back from 3 to anything.

0123
2 → 3 reaches a visited node, but 3 was finished long ago. Two paths to the same place are not a cycle.

What a cycle needs is an edge back to a node that is on the current path: an ancestor that the search has entered and not yet left. Following that edge leads back to where the path began.

Three colours

Give every node one of three states:

  • White: not visited yet.
  • Grey: entered, not finished. The grey nodes are exactly the current path from the start to here.
  • Black: finished. Everything reachable from it has been explored, and none of it led back to the path.

An edge to a grey node closes a cycle. An edge to a black node is safe to ignore: if a cycle ran through a black node, the search would have found it while that node was grey.

Cycle in a directed graph

visit(u)

  1. Colour u grey.
  2. For each v with an edge u → v: if v is grey, return true; if v is white and visit(v) is true, return true.
  3. Colour u black and return false.

main

  1. For every white node s: if visit(s), return true.
  2. Return false.

Step by step

A graph with a cycle 3 → 4 → 5 → 3, reached from 0 along two different routes:

Grey nodes form the current path; the search stops at the first edge into a grey node.

  1. Step 1 / 6
    012345
    grey: the current path
    00

    Colour 0 grey: it is on the current path.

  2. Step 2 / 6
    012345
    grey: the current path
    0011

    Colour 1 grey: it is on the current path.

  3. Step 3 / 6
    012345
    grey: the current path
    001132

    Colour 3 grey: it is on the current path.

  4. Step 4 / 6
    012345
    grey: the current path
    00113243

    Colour 4 grey: it is on the current path.

  5. Step 5 / 6
    012345
    grey: the current path
    0011324354

    Colour 5 grey: it is on the current path.

  6. Step 6 / 6
    012345
    grey: the current path
    0011324354

    5 → 3 reaches a grey node, one still on the current path. The path from 3 to 5 plus this edge is a cycle.

current nodegrey: on the current pathblack: finishededge into a grey node

Implementation

Three states in an integer array: 0 white, 1 grey, 2 black.

import sys

def has_directed_cycle(n, edges):
    sys.setrecursionlimit(10000)
    adj = [[] for _ in range(n)]
    for u, v in edges:
        adj[u].append(v)
    WHITE, GREY, BLACK = 0, 1, 2
    colour = [WHITE] * n

    def visit(u):
        colour[u] = GREY                 # u is on the current path
        for v in adj[u]:
            if colour[v] == GREY:
                return True              # back along the path: a cycle
            if colour[v] == WHITE and visit(v):
                return True
        colour[u] = BLACK                # finished, and no cycle through it
        return False

    return any(colour[s] == WHITE and visit(s) for s in range(n))

The same code is the reference solution to Does a directed graph contain a cycle?, where it is compiled and run against the tests in all six languages.

TimeO(n + m)
SpaceO(n)

Each node changes colour twice and each edge is examined once. The recursion can be n deep; for very long chains, use the in-degree method from the next module, which needs no recursion.

The order in which the search colours nodes black is useful in its own right: reversed, it is a topological order of the graph. The next module builds on exactly that.