#802 Find Eventual Safe States
the problem
In plain words
graph[i] lists the nodes node i points to. A terminal node has no outgoing edges. A node is safe if every path from it ends at a terminal node. Return all safe nodes in ascending order.
in graph = [[1,2],[2,3],[5],[0],[5],[],[]]
out [2,4,5,6]
0, 1, 3 can loop 0→1→3→0 forever.
in graph = [[1,2,3,4],[1,2],[3,4],[0,4],[]]
out [4]
- 1 ≤ n ≤ 10⁴
- 0 ≤ graph[i].length ≤ n
- graph[i] is strictly increasing
- Total edges ≤ 4·10⁴
your turn
Try it before you peek
Read the examples, say the brute force out loud, then write code: here or on LeetCode ↗. Stuck for 20 minutes? Open one hint at a time.
Solve it in the browser
LeetCode-style: complete the class. Compiled by the real javac (Java 17) and judged right here — nothing is sent anywhere.
Solutions are hidden so you can try first. Even 20 minutes of real effort makes the answer stick far better than reading it.
Marking the problem solved, or opening every hint, also shows it.
the aha
Key insight
patternTopological sort & cycle detection
Unsafe = can reach a cycle. In DFS colours, a vertex is safe exactly when it turns black; anything left gray touched a cycle.
solutions
From brute force to optimal
Reuse cycle-detection DFS: a node is safe iff all its neighbours are safe; meeting a gray node means a cycle.
- isSafe(u): if coloured, return colour == black.
- Paint gray; if any neighbour is unsafe, return false (u stays gray).
- Paint black, return true.
- Collect safe nodes in index order.
Each node is resolved once thanks to the colour memo.
Colour array plus recursion depth up to V.
class Solution { public List<Integer> eventualSafeNodes(int[][] graph) { int n = graph.length; int[] color = new int[n]; // 0 unvisited, 1 visiting (on path), 2 safe List<Integer> safe = new ArrayList<>(); for (int v = 0; v < n; v++) if (isSafe(v, graph, color)) safe.add(v); // ascending for free return safe; } private boolean isSafe(int u, int[][] graph, int[] color) { if (color[u] != 0) return color[u] == 2; // gray = on a cycle (or leads into one) color[u] = 1; for (int v : graph[u]) { if (!isSafe(v, graph, color)) return false; // u stays gray forever = unsafe } color[u] = 2; return true; }}Flip every edge. Terminal nodes become sources. A node is safe once all of its original exits are known safe.
- Reverse edges; outdegree[u] = graph[u].length.
- Queue nodes with outdegree 0 (terminal).
- Pop v (safe); for each u pointing to v, decrement outdegree[u]; enqueue at 0.
- Return safe nodes ascending.
Each reversed edge is processed once.
The reversed adjacency list.
class Solution { public List<Integer> eventualSafeNodes(int[][] graph) { int n = graph.length; List<List<Integer>> reverse = new ArrayList<>(); for (int i = 0; i < n; i++) reverse.add(new ArrayList<>()); int[] outdegree = new int[n]; for (int u = 0; u < n; u++) { outdegree[u] = graph[u].length; for (int v : graph[u]) reverse.get(v).add(u); // flip every edge } Deque<Integer> queue = new ArrayDeque<>(); for (int v = 0; v < n; v++) if (outdegree[v] == 0) queue.offer(v); // terminal nodes boolean[] safe = new boolean[n]; while (!queue.isEmpty()) { int v = queue.poll(); safe[v] = true; for (int u : reverse.get(v)) { if (--outdegree[u] == 0) queue.offer(u); // all of u's exits are safe now } } List<Integer> result = new ArrayList<>(); for (int v = 0; v < n; v++) if (safe[v]) result.add(v); return result; }}in the room
How to say it in the interview
talk track
- Translate: "safe" means no path reaches a cycle. Then either reuse colour DFS or run Kahn backwards from the terminals.
traps
- Resetting gray nodes to white after a failed DFS — they must stay gray (unsafe) so later lookups are O(1) and correct.
what they ask next
Follow-ups
A path of 10⁴ nodes could make the DFS 10⁴ calls deep. How do you avoid that?
Use the reversed-graph Kahn version: it is a plain loop with a queue, so there is no recursion at all. Same O(V + E).
Return the unsafe nodes instead?
They are the complement: the nodes left gray after the DFS, or the nodes Kahn never dequeues on the reversed graph.
before you submit
Edge cases to test
- A self-loop makes that node unsafe.
- A terminal node (no outgoing edges) is safe.
- A node with one edge to a terminal and one into a cycle is unsafe — every path must end.
practise next
Similar problems
- #207Course ScheduleMediumin Topo Sort
- #210Course Schedule IIMediumin Topo Sort
- #141Linked List CycleEasyin Linked Lists