Find Eventual Safe States Using Topological Sort (BFS) - Visualization

Problem Statement

In a directed graph with V vertices and E edges (represented as an adjacency list adj), each node is uniquely labeled from 0 to V - 1.

A node is called a terminal node if it has no outgoing edges. A node is a safe node if every possible path starting from that node eventually leads to a terminal node.

The task is to return a sorted list of all such safe nodes.

Examples

Adjacency List Safe Nodes Description
[[1,2],[2,3],[5],[0],[5],[],[]] [2,4,5,6] Only nodes that lead to terminal nodes (5 and 6) are safe
[[],[0,2,3,4],[3],[4],[]] [0,1,2,3,4] All nodes lead to terminal node 0 or 4
[[1],[2],[0]] [] Cycle exists, no node is safe
[[],[],[]] [0,1,2] All nodes are terminal and hence safe
[[],[0],[1]] [0,1,2] Each node leads to a terminal node

Solution

Understanding the Problem

In this problem, we are given a directed graph represented as an adjacency list. Our goal is to find all the eventual safe states.

An eventual safe state is a node from which any possible path will always lead to a terminal node (a node with no outgoing edges) and never enter a cycle. If a node can reach a cycle, it is not considered safe.

This means we must find all nodes that do not participate in or lead to a cycle in any way.

Step-by-Step Solution with Example

Step 1: Understand the graph structure

Let’s consider the example: graph = [[1,2],[2,3],[5],[0],[5],[],[]]

Each index represents a node, and the array at each index lists the nodes it points to. For instance, node 0 points to nodes 1 and 2.

Step 2: Reverse the graph

To track which nodes lead to which, we build a reversed graph. In the reversed graph, for every edge u → v in the original graph, we add an edge v → u.

This helps us track who depends on which node, making it easier to determine when a node becomes safe.

Step 3: Track out-degrees

We maintain an array where outdegree[i] stores the number of outgoing edges from node i in the original graph.

Terminal nodes (with no outgoing edges) will have outdegree 0. These are our starting point — they are trivially safe.

Step 4: Use a queue to process safe nodes

We put all terminal nodes in a queue and process them one by one. For each node removed from the queue, we go through its parents in the reversed graph and reduce their out-degree by 1.

If a parent’s out-degree becomes 0, it also becomes safe, so we add it to the queue.

Step 5: Collect and sort safe nodes

All nodes whose out-degree becomes 0 during this process are eventually safe. We collect them, sort them, and return them as the final answer.

Edge Cases

  • Empty graph: If the input graph has no nodes, the result should be an empty list.
  • All terminal nodes: If every node has no outgoing edges, all are safe.
  • All nodes in a cycle: If every node is part of a cycle, no node is safe.
  • Disconnected components: The algorithm handles each component independently. Safe nodes from different components are still detected correctly.

Finally

This problem is best understood by thinking in reverse: instead of finding which nodes can reach terminal states, we ask which nodes can be reached from terminal states without forming a cycle.

By reversing the graph and tracking out-degrees, we simulate a safe-state propagation, slowly marking each node safe once all its paths are known to be safe. This approach ensures an intuitive and efficient solution suitable even for large graphs.

Algorithm Steps

  1. Reverse the graph: for each edge u → v, add u to revAdj[v].
  2. Initialize an outDegree[] array where each element is the number of outgoing edges from a node in the original graph.
  3. Create a queue and enqueue all nodes with outDegree = 0 (terminal nodes).
  4. While the queue is not empty:
    1. Dequeue a node node.
    2. For each neighbor in revAdj[node]:
      1. Reduce outDegree[neighbor] by 1.
      2. If outDegree[neighbor] == 0, enqueue neighbor.
  5. All nodes with outDegree == 0 at the end are safe. Return them sorted.

Code

C
C++
Python
Java
JS
Go
Rust
Kotlin
Swift
TS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX 100

void reverseGraph(int V, int adj[][MAX], int revAdj[][MAX], int outDegree[], int size[]) {
    for (int u = 0; u < V; u++) {
        for (int j = 0; j < size[u]; j++) {
            int v = adj[u][j];
            revAdj[v][size[v]++] = u;
            outDegree[u]++;
        }
    }
}

void eventualSafeNodes(int V, int adj[][MAX], int size[]) {
    int revAdj[MAX][MAX] = {0};
    int revSize[MAX] = {0};
    int outDegree[MAX] = {0};
    bool safe[MAX] = {false};
    int queue[MAX], front = 0, rear = 0;

    reverseGraph(V, adj, revAdj, outDegree, size);

    for (int i = 0; i < V; i++) {
        if (outDegree[i] == 0) queue[rear++] = i;
    }

    while (front < rear) {
        int node = queue[front++];
        safe[node] = true;
        for (int j = 0; j < revSize[node]; j++) {
            int neighbor = revAdj[node][j];
            if (--outDegree[neighbor] == 0) {
                queue[rear++] = neighbor;
            }
        }
    }

    printf("Safe Nodes: ");
    for (int i = 0; i < V; i++) {
        if (safe[i]) printf("%d ", i);
    }
    printf("\n");
}

int main() {
    int V = 7;
    int adj[MAX][MAX] = {
        {1, 2}, {2, 3}, {5}, {0}, {5}, {}, {}
    };
    int size[MAX] = {2, 2, 1, 1, 1, 0, 0};

    eventualSafeNodes(V, adj, size);
    return 0;
}

Time Complexity

CaseTime ComplexityExplanation
Best CaseO(V + E)Even in the best case (e.g., an acyclic graph), we must traverse all vertices and edges to identify safe nodes.
Average CaseO(V + E)Each node and edge is visited once, whether it's part of a cycle or not.
Worst CaseO(V + E)In graphs with deep chains or many dependencies, the traversal still touches all vertices and edges once.

Space Complexity

O(V + E)

Explanation: We store the reversed adjacency list, out-degree array, and queue, all of which depend on the number of vertices and edges.


Comments

💬 Please keep your comment relevant and respectful. Avoid spamming, offensive language, or posting promotional/backlink content.
All comments are subject to moderation before being published.


Loading comments...