Problem Statement

Brute Force [TLE]
class Solution:
def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]:
N = len(graph)
queue = deque()
safe_nodes = {i: False for i in range(N)}
adj_list = {i: [] for i in range(N)}
for i, nodes in enumerate(graph):
if not nodes:
queue.append(i)
safe_nodes[i] = True
for node in nodes:
adj_list[node].append(i)
while queue:
node = queue.popleft()
for nei in adj_list[node]:
if all(safe_nodes[x] for x in graph[nei]):
safe_nodes[nei] = True
queue.append(nei)
return [i for i in range(N) if safe_nodes[i]]
Improved Algorithm [Accepted]
N = len(graph)
# Reverse the graph and calculate outdegrees
reversed_graph = {i: [] for i in range(N)}
outdegree = [0] * N
for u, neighbors in enumerate(graph):
outdegree[u] = len(neighbors)
for v in neighbors:
reversed_graph[v].append(u)
# Queue for nodes with no outgoing edges (safe nodes)
queue = deque([i for i in range(N) if outdegree[i] == 0])
safe_nodes = []
# Process the queue
while queue:
node = queue.popleft()
safe_nodes.append(node)
for prev_node in reversed_graph[node]:
outdegree[prev_node] -= 1
if outdegree[prev_node] == 0:
queue.append(prev_node)
# Return sorted list of safe nodes
return sorted(safe_nodes)
Editorial
Approach 1: Topological Sort Using Kahn’s Algorithm
class Solution:
def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]:
n = len(graph)
indegree = [0] * n
adj = [[] for _ in range(n)]
for i in range(n):
for node in graph[i]:
adj[node].append(i)
indegree[i] += 1
q = deque()
# Push all the nodes with indegree zero in the queue.
for i in range(n):
if indegree[i] == 0:
q.append(i)
safe = [False] * n
while q:
node = q.popleft()
safe[node] = True
for neighbor in adj[node]:
# Delete the edge "node -> neighbor".
indegree[neighbor] -= 1
if indegree[neighbor] == 0:
q.append(neighbor)
safeNodes = []
for i in range(n):
if safe[i]:
safeNodes.append(i)
return safeNodes
Approach 2: Depth First Search
class Solution:
def dfs(self, node, adj, visit, inStack):
# If the node is already in the stack, we have a cycle.
if inStack[node]:
return True
if visit[node]:
return False
# Mark the current node as visited and part of current recursion stack.
visit[node] = True
inStack[node] = True
for neighbor in adj[node]:
if self.dfs(neighbor, adj, visit, inStack):
return True
# Remove the node from the stack.
inStack[node] = False
return False
def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]:
n = len(graph)
visit = [False] * n
inStack = [False] * n
for i in range(n):
self.dfs(i, graph, visit, inStack)
safeNodes = []
for i in range(n):
if not inStack[i]:
safeNodes.append(i)
return safeNodes