~/problems / Graphs / Cycle detection

Find Eventual Safe States

medium ~25 min

Implement eventual_safe_nodes(graph) -> list[int].

graph is a directed graph on nodes 0..n-1 given as adjacency lists: graph[i] lists the nodes that i has an edge to. Self-loops are allowed. A terminal node has no outgoing edges. A node is safe if every path that starts from it eventually reaches a terminal node, which means no path from it ever reaches a cycle. Return all safe nodes in increasing order.

eventual_safe_nodes([[1], [2], [0, 3], [], [4], [3]])
# [3, 5]
# 0 -> 1 -> 2 -> 0 is a cycle, so 0, 1 and 2 are unsafe. 3 is terminal.
# 4 has a self-loop, so it's unsafe. 5 only leads to 3, so it's safe.

n goes up to 10,000, and a separate search from every node is too slow on a long chain: aim for O(n + m), where m is the number of edges. Chains can be 10,000 long, so avoid deep recursion.

Show hint

Work backwards from the terminal nodes: a node is safe exactly when all of its out-edges lead to nodes already known to be safe.

Topic: Cycle detection. Visited / on-stack sets; symlinks, CNAMEs, instruction loops.

Read the visual guide
0:00
Ctrl ' run · Ctrl ↵ submit
esc