Before attempting this problem, you should be comfortable with:
A node is safe if every path starting from it eventually leads to a terminal node (a node with no outgoing edges). Conversely, a node is unsafe if it can reach a cycle. We can use DFS with memoization to determine safety: initially mark a node as unsafe when we start exploring it (to detect cycles), then mark it safe only if all its neighbors are safe. If we revisit a node marked unsafe during exploration, we have found a cycle.
safe to track each node's safety status.false (unsafe) initially to detect cycles.false.true (safe).dfs(node) returns true and return them in order.class Solution:
def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]:
n = len(graph)
safe = {}
def dfs(node):
if node in safe:
return safe[node]
safe[node] = False
for nei in graph[node]:
if not dfs(nei):
return safe[node]
safe[node] = True
return safe[node]
res = []
for node in range(n):
if dfs(node):
res.append(node)
return resWhere is the number of vertices and is the number of edges in the given graph.
Think of safe nodes as those that can reach terminal nodes. If we reverse the edge directions, safe nodes are those reachable from terminal nodes. Using Kahn's algorithm on this reversed perspective: terminal nodes have zero out-degree in the original graph. We start from these terminal nodes and work backward, removing edges. Any node whose out-degree reaches zero is safe because all its paths lead to already-confirmed safe nodes.
parents) and track the out-degree of each node.0) to a queue.0, add it to the queue.0 are safe.class Solution:
def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]:
n = len(graph)
outdegree = [0] * n
parents = [[] for _ in range(n)]
queue = deque()
for node in range(n):
outdegree[node] = len(graph[node])
if outdegree[node] == 0:
queue.append(node)
for nei in graph[node]:
parents[nei].append(node)
while queue:
node = queue.popleft()
for parent in parents[node]:
outdegree[parent] -= 1
if outdegree[parent] == 0:
queue.append(parent)
res = []
for node in range(n):
if outdegree[node] <= 0:
res.append(node)
return resWhere is the number of vertices and is the number of edges in the given graph.
A node being visited during DFS does not mean it is safe. You need separate tracking for "currently being explored" (to detect cycles) and "confirmed safe" (all paths lead to terminal nodes). Mixing these states causes incorrect cycle detection or premature safety classification.
The problem requires returning safe nodes in ascending order. If you collect nodes as they are confirmed safe during DFS, the order depends on traversal order, not node indices. Either sort the result at the end or iterate through nodes 0 to n-1 and check each one's safety status.
Terminal nodes (nodes with no outgoing edges) are always safe by definition. In the topological sort approach, these nodes have out-degree 0 and should be the starting points. In DFS, they should immediately return true without further recursion.