1 minute read

Problem Statement

problem

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
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