Find Eventual Safe states


Problem Statement

Link : Find eventual safe states

A directed graph is given as graph, where graph[i] lists all nodes reachable directly from node i.

Return all safe nodes in ascending order


Intuition

A node is safe if it cannot reach a cycle via any path. Terminal nodes are inherently safe.
We can reverse the graph and apply a Kahn-style topological peel: start from terminal nodes and propagate safety upstream. Nodes that feed cycles will never drop to indegree zero and thus remain unsafe

Alternatively, use DFS coloring: mark nodes during recursion to detect cycles; only paths avoiding cycles result in safe nodes


Approach 1: Reverse-Graph + Kahn’s Algorithm (BFS)

Steps

  1. Build reversed adjacency list: for edge u → v in original graph, add v → u.

  2. Compute indegree for each node based on original out-degrees.

  3. Initialize a queue with all terminal nodes (indegree = 0).

  4. While queue not empty:

    • Pop a safe node.

    • For each predecessor in reversed graph:

      • Decrement its indegree (i.e. one outgoing path now proven safe).

      • If indegree becomes 0, add it to queue.

  5. Nodes collected are safe. Sort and return

Java‑style Code

class Solution {
    public List<Integer> eventualSafeNodes(int[][] graph) {
        int N = graph.length;
        List<List<Integer>> rev = new ArrayList<>();
        for (int i = 0; i < N; i++) rev.add(new ArrayList<>());
        
        int[] indegree = new int[N];
        // Build reversed graph
        for (int i = 0; i < N; i++) {
            for (int j : graph[i]) {
                rev.get(j).add(i);
                indegree[i]++;
            }
        }

        Queue<Integer> q = new LinkedList<>();
        for (int i = 0; i < N; i++) {
            if (indegree[i] == 0)
                q.add(i);
        }

        List<Integer> safe = new ArrayList<>();
        while (!q.isEmpty()) {
            int node = q.poll();
            safe.add(node);
            for (int prev : rev.get(node)) {
                if (--indegree[prev] == 0)
                    q.add(prev);
            }
        }

        Collections.sort(safe);
        return safe;
    }
}

Dry Run

graph = [[1,2],[2,3],[5],[0],[5],[],[]]

Complexity


Approach 2: DFS with Coloring

Intuition

Use node coloring to track DFS state:

DFS a node:

Java‑style Code

class Solution {
    List<Integer> res = new ArrayList<>();
    int[] color; // 0=unvisited, 1=visiting, 2=safe

    public List<Integer> eventualSafeNodes(int[][] graph) {
        int N = graph.length;
        color = new int[N];
        for (int i = 0; i < N; i++) {
            if (dfs(graph, i))
                res.add(i);
        }
        Collections.sort(res);
        return res;
    }

    private boolean dfs(int[][] graph, int node) {
        if (color[node] != 0)
            return color[node] == 2;
        color[node] = 1; // start exploring
        for (int nei : graph[node]) {
            if (!dfs(graph, nei))
                return false;
        }
        color[node] = 2; // confirmed safe
        return true;
    }
}

Dry Run

graph = [[1,2],[2,3],[5],[0],[5],[],[]]

Complexity


Conclusion and Comparison

Method Time Space Explanation
BFS (Kahn’s Algorithm) O(V + E) O(V + E) Reverse graph + topological peeling from terminal nodes
DFS Coloring O(V + E) O(V) DFS with cycle detection and coloring