Find Eventual Safe States solution and explanation
9 more videos on this problem
G-20. Find Eventual Safe States - DFStake U forward · 23:43 · 318K views
G-25. Find Eventual Safe States - BFS - Topological Sorttake U forward · 16:57 · 298K views
Find Eventual Safe States - Leetcode 802 - PythonNeetCode · 13:18 · 35K views
Find Eventual Safe States | 2 Approaches | Intuition | Leetcode 802 | MICROSOFT | codestorywithMIKcodestorywithMIK · 33:54 · 20K views
Find Eventual Safe States - Leetcode 802 - PythonNeetCodeIO · 13:18 · 9K views
Find Eventual Safe States | Leetcode 802Techdose · 20:44 · 3K views
Leetcode 802. Find Eventual Safe States. EnglishAlina L · 9:15 · 1K views
802. Find Eventual Safe StatesDeep Tech · 21:17 · 631 views
802. Find Eventual Safe States | Leetcode Daily ChallengeEthos Typos · 15:04 · 169 views
LeetCode - 802. Find Eventual Safe States | DFS | JavaOrkhan Gasanov · 8:40 · 124 views
The problem
There is a directed graph of n nodes with each node labeled from 0 to n - 1. The graph is represented by a 0-indexed 2D integer array graph where graph[i] is an integer array of nodes adjacent to node i, meaning there is an edge from node i to each node in graph[i].
A node is a terminal node if there are no outgoing edges. A node is a safe node if every possible path starting from that node leads to a terminal node (or another safe node).
Return an array containing all the safe nodes of the graph. The answer should be sorted in ascending order.
Example 1:
Input: graph = [[1,2],[2,3],[5],[0],[5],[],[]] Output: [2,4,5,6] Explanation: The given graph is shown above. Nodes 5 and 6 are terminal nodes as there are no outgoing edges from either of them. Every path starting at nodes 2, 4, 5, and 6 all lead to either node 5 or 6.
Example 2:
Input: graph = [[1,2,3,4],[1,2],[3,4],[0,4],[]] Output: [4] Explanation: Only node 4 is a terminal node, and every path starting at node 4 leads to node 4.
Constraints:
n == graph.length1 <= n <= 1040 <= graph[i].length <= n0 <= graph[i][j] <= n - 1graph[i]is sorted in a strictly increasing order.- The graph may contain self-loops.
- The number of edges in the graph will be in the range
[1, 4 * 104].
Approach overview
Problem Overview: You are given a directed graph where graph[i] lists all nodes reachable from node i. A node is considered eventually safe if every possible path starting from it ends at a terminal node (a node with no outgoing edges). Any node that can reach a cycle is unsafe. The task is to return all safe nodes in sorted order.
Approach 1: Reverse Graph and Topological Sort (O(V + E) time, O(V + E) space)
This approach converts the problem into a reverse topological process. First build the reverse graph so that every edge u → v becomes v → u. At the same time track the outdegree of each node in the original graph. Terminal nodes naturally have outdegree 0, so they are guaranteed safe. Push them into a queue and process them using a BFS-style topological sort. Every time you remove a safe node, decrease the outdegree of its predecessors in the reverse graph. If a predecessor’s outdegree becomes 0, it also becomes safe and enters the queue. The key insight: nodes that eventually lead only to safe nodes will lose all outgoing edges during this process. This solution is essentially a reversed topological sort applied to a graph, making it highly efficient and easy to reason about.
Approach 2: Depth-First Search with Safe Node Identification (O(V + E) time, O(V) space)
This method uses DFS to detect cycles and classify nodes. Maintain a state array with three states: unvisited, visiting, and safe. During DFS, mark the node as visiting. If you encounter another visiting node, a cycle exists and the path is unsafe. If all neighbors eventually lead to safe nodes, mark the current node as safe and return true. Memoization ensures each node is processed once. This is a classic cycle-detection pattern using Depth-First Search. The algorithm avoids recomputation by caching node states, giving linear complexity relative to nodes and edges.
Recommended for interviews: The reverse graph + topological sort solution is typically preferred. It clearly models the problem as eliminating nodes that cannot participate in cycles, and the BFS queue naturally reveals safe nodes. Interviewers often expect you to recognize the connection between cycle detection and topological processing. The DFS approach is also valid and demonstrates strong understanding of recursion and graph state tracking, but the topological strategy tends to be easier to explain and reason about under interview pressure.
Approach 1: Approach 1: Reverse Graph and Topological Sort
This approach utilizes the concept of reversing the graph and then performing a topological sort. Essentially, if we consider nodes leading to terminal nodes in the original graph, these nodes can be treated as terminal nodes in the reversed graph. We perform a topological sort to find such nodes.
By processing nodes which have no outgoing edges in the reversed graph (these correspond to safe nodes in the original graph), we can iteratively determine more nodes that are safe.
from collections import deque, defaultdict
def eventualSafeNodes(graph):
G = defaultdict(list)
rg = defaultdict(list)
outdegree = [0] * len(graph)
for u in range(len(graph)):
for v in graph[u]:
G[u].append(v)
rg[v].append(u)
outdegree[u] = len(graph[u])
q = deque([])
for i in range(len(graph)):
if outdegree[i] == 0:
q.append(i)
res = []
while q:
u = q.popleft()
res.append(u)
for v in rg[u]:
outdegree[v] -= 1
if outdegree[v] == 0:
q.append(v)
return sorted(res)
graph = [[1, 2], [2, 3], [5], [0], [5], [], []]
print(eventualSafeNodes(graph))import java.util.*;
class Solution {
public List<Integer> eventualSafeNodes(int[][] graph) {
// Implementation
return new ArrayList<>();
}
public static void main(String[] args) {
Solution sol = new Solution();
int[][] graph = {{1, 2}, {2, 3}, {5}, {0}, {5}, {}, {}};
List<Integer> result = sol.eventualSafeNodes(graph);
System.out.println(result);
}
}#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
void findEventualSafeNodes(int** graph, int graphSize, int* graphColSize, int* returnSize) {
// Implementation
}
int main() {
int n = 7;
int graphColSize[] = {2, 2, 1, 1, 1, 0, 0};
int graphData[7][2] = {{1, 2}, {2, 3}, {5}, {0}, {5}, {}, {}};
int* graph[7];
for (int i = 0; i < 7; i++) {
graph[i] = graphData[i];
}
int returnSize = 0;
findEventualSafeNodes(graph, 7, graphColSize, &returnSize);
return 0;
}This function utilizes BFS with reversed graph edges. Nodes with zero out-degree in the reversed graph are safe, and propagating this information allows identification of additional safe nodes.
Time Complexity: O(V + E); Space Complexity: O(V + E), due to additional structures used to track reversed graph edges and node degrees.
Approach 2: Approach 2: Depth-First Search with Safe Node Identification
This approach adopts a classic depth-first search (DFS) to identify which nodes are safe. Safe nodes are marked during traversal.
We maintain a visited state for each node: unvisited, visiting, or safe. During DFS traversal, if we encounter a node marked as visiting, a cycle is detected, and the path is unsafe.
#include <vector>
#include <iostream>
using namespace std;
vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
// Implementation
return {};
}
int main() {
vector<vector<int>> graph = {{1, 2}, {2, 3}, {5}, {0}, {5}, {}, {}};
vector<int> result = eventualSafeNodes(graph);
for (int node : result) {
cout << node << " ";
}
return 0;
}var eventualSafeNodes = function(graph) {
// Implementation
return [];
};
let graph = [[1, 2], [2, 3], [5], [0], [5], [], []];
console.log(eventualSafeNodes(graph));using System;
using System.Collections.Generic;
public class Solution {
public IList<int> EventualSafeNodes(int[][] graph) {
// Implementation
return new List<int>();
}
public static void Main(string[] args) {
Solution sol = new Solution();
int[][] graph = new int[][] { new int[] { 1, 2 }, new int[] { 2, 3 }, new int[] { 5 }, new int[] { 0 }, new int[] { 5 }, new int[0], new int[0]};
var result = sol.EventualSafeNodes(graph);
Console.WriteLine(String.Join(", ", result));
}
}The function leverages DFS to maintain a status for each node: safe, unsafe, or unknown. It handles cycles efficiently by marking nodes actively being visited and recording safe nodes on successful completion of DFS for a node.
Time Complexity: O(V + E), as each node and each edge is examined only once.
Space Complexity: O(V) for the recursion stack and state storage.
Approach 3: Default Approach
func eventualSafeNodes(graph [][]int) []int {
n := len(graph)
indeg := make([]int, n)
rg := make([][]int, n)
q := []int{}
for i, vs := range graph {
for _, j := range vs {
rg[j] = append(rg[j], i)
}
indeg[i] = len(vs)
if indeg[i] == 0 {
q = append(q, i)
}
}
for len(q) > 0 {
i := q[0]
q = q[1:]
for _, j := range rg[i] {
indeg[j]--
if indeg[j] == 0 {
q = append(q, j)
}
}
}
ans := []int{}
for i, v := range indeg {
if v == 0 {
ans = append(ans, i)
}
}
return ans
}Complexity comparison
| Approach | Complexity |
|---|---|
| Approach 1: Reverse Graph and Topological Sort | Time Complexity: O(V + E); Space Complexity: O(V + E), due to additional structures used to track reversed graph edges and node degrees. |
| Approach 2: Depth-First Search with Safe Node Identification | Time Complexity: O(V + E), as each node and each edge is examined only once. Space Complexity: O(V) for the recursion stack and state storage. |
| Default Approach | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Reverse Graph + Topological Sort (BFS) | O(V + E) | O(V + E) | Best general solution. Efficient for detecting nodes not connected to cycles in directed graphs. |
| DFS with Safe Node Memoization | O(V + E) | O(V) | Useful when implementing recursive cycle detection or when DFS is already part of the solution. |
Frequently asked questions
Is Find Eventual Safe States easy or hard?
Find Eventual Safe States is classified as a Medium problem. The main challenge is recognizing that nodes leading to cycles must be excluded and that reversing the graph allows a topological elimination process.
Find Eventual Safe States Python/Java solution
Most implementations build a reversed graph and perform a BFS topological sort. Python uses lists and collections.deque for the queue, while Java commonly uses ArrayList and ArrayDeque. Both implementations achieve O(V + E) time complexity.
How to solve Find Eventual Safe States in O(V + E)?
Build a reverse adjacency list and track the outdegree of every node. Start with nodes whose outdegree is zero (terminal nodes) and push them into a queue. Process them using BFS, reducing the outdegree of their predecessors. When a predecessor’s outdegree becomes zero, it becomes safe and enters the queue.
What is the best approach for Find Eventual Safe States?
Reverse graph with topological sort is the most common solution. By reversing edges and tracking node outdegrees, terminal nodes are processed first using a BFS queue. Nodes that eventually lose all outgoing edges become safe. This runs in O(V + E) time and is widely expected in interviews.
Is Find Eventual Safe States asked at Google/Amazon/Meta?
Graph cycle detection and topological sorting problems appear frequently at companies like Google, Amazon, and Meta. Variations of this problem are used to test understanding of directed graphs, dependency resolution, and cycle detection.
What data structure is used in Find Eventual Safe States?
The main structures are adjacency lists for the graph representation, a queue for BFS processing in the topological approach, and arrays for tracking node states or outdegrees. In the DFS version, recursion and a state array track visiting and safe nodes.
What is the time complexity of Find Eventual Safe States?
Both the topological sort and DFS approaches run in O(V + E) time, where V is the number of nodes and E is the number of edges. Each node and edge is processed at most once. Space complexity ranges from O(V) to O(V + E) depending on whether a reverse graph is stored.
