1557. Minimum Number of Vertices to Reach all Nodes - Explanation
Description
You are given a directed acyclic graph with n nodes labeled 0 through n - 1. Each pair [from, to] in edges represents a directed edge from from to to.
Return a smallest set of starting nodes from which every node can be reached by following directed edges. A starting node is considered reachable from itself.
The smallest set is unique. Return its node labels in any order; do not include duplicates.
Example 1:
Input: n = 5
edges = [[0,1],[2,1],[1,3]]
Output: [0,2,4]Explanation: Start from 0, 2, and the isolated node 4. Nodes 1 and 3 are reachable from 0 or 2. None of 0, 2, or 4 can be reached from another node, so all three are necessary.
Example 2:
Input: n = 4
edges = [[3,2],[2,1],[1,0]]
Output: [3]Explanation: Starting from node 3 reaches the chain 3 → 2 → 1 → 0, covering every node with one starting point.
Example 3:
Input: n = 5
edges = [[0,4],[1,4],[2,4],[3,4]]
Output: [0,1,2,3]Explanation: Nodes 0, 1, 2, and 3 cannot be reached from any other node, so each must be chosen. Node 4 is then reachable from any of them.
Constraints:
2 <= n <= 1000001 <= edges.length <= min(100000, n * (n - 1) / 2)edges[i].length == 2- Both endpoints are node labels between
0andn - 1. - All directed edges are distinct.
- The graph has no directed cycles.
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Graph Representation (Adjacency List) - Required to store and traverse the directed graph
- Depth First Search (DFS) - One approach uses DFS to identify reachable nodes
- Indegree Concept - The optimal solution identifies nodes with zero incoming edges
1. Depth First Search
Intuition
We want to find the smallest set of vertices from which all other vertices are reachable. A vertex must be in this set if no other vertex can reach it, meaning it has no incoming edges.
Using dfs, we traverse from each unvisited node and mark all reachable nodes. Any node that gets reached from another node can be removed from our candidate set. The nodes that remain are those with no incoming edges.
Algorithm
- Build an adjacency list from the edges.
- Initialize a set containing all vertices as potential starting points.
- For each unvisited vertex, run
dfs:- Mark the vertex as visited.
- For each neighbor, recursively visit it and remove it from the
resset.
- Return the remaining vertices in the set.
class Solution:
def findSmallestSetOfVertices(self, n: int, edges: List[List[int]]) -> List[int]:
adj = [[] for _ in range(n)]
for u, v in edges:
adj[u].append(v)
res = set(range(n))
visited = [False] * n
def dfs(node):
visited[node] = True
for nei in adj[node]:
if not visited[nei]:
dfs(nei)
res.discard(nei)
for i in range(n):
if not visited[i]:
dfs(i)
return list(res)Time & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of vertices and is the number of edges.
2. Iterative DFS
Intuition
This is the same approach as recursive dfs but uses an explicit stack to avoid recursion. We process nodes iteratively, marking each visited node and removing any node that has an incoming edge from our candidate set.
Algorithm
- Build an adjacency list from the edges.
- Initialize a boolean array where all vertices are potential
res. - Use a
stackfor iterativedfs:- Pop a
nodefrom thestack. - Skip if already visited; otherwise mark as visited.
- For each neighbor, add to
stackif unvisited and mark as not ares.
- Pop a
- Collect all vertices still marked as
res.
class Solution:
def findSmallestSetOfVertices(self, n: int, edges: List[List[int]]) -> List[int]:
adj = [[] for _ in range(n)]
for u, v in edges:
adj[u].append(v)
res = [True] * n
visited = [False] * n
stack = []
for i in range(n):
if not visited[i]:
stack.append(i)
while stack:
node = stack.pop()
if visited[node]:
continue
visited[node] = True
for nei in adj[node]:
if not visited[nei]:
stack.append(nei)
res[nei] = False
return [i for i in range(n) if res[i]]Time & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of vertices and is the number of edges.
3. Indegree Count
Intuition
A vertex needs to be in our starting set only if it cannot be reached from any other vertex. This happens exactly when the vertex has no incoming edges. We can track which vertices have incoming edges by building a list of sources for each destination.
Algorithm
- Create an array of lists where
incoming[v]stores all vertices with edges pointing tov. - Iterate through all edges and populate the
incominglists. - Collect all vertices where
incoming[v]is empty. - Return these vertices as the
res.
class Solution:
def findSmallestSetOfVertices(self, n: int, edges: List[List[int]]) -> List[int]:
incoming = collections.defaultdict(list)
for src, dst in edges:
incoming[dst].append(src)
res = []
for i in range(n):
if not incoming[i]:
res.append(i)
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of vertices and is the number of edges.
4. Indegree Count (Optimal)
Intuition
We do not need to store the actual source vertices for each destination. We only care whether a vertex has at least one incoming edge. A simple boolean array is sufficient: mark each destination as having an incoming edge, then return all vertices that were never marked.
Algorithm
- Create a boolean array
indegreeof sizen, initialized tofalse. - For each edge
(src, dst), setindegree[dst] = true. - Collect all vertices
iwhereindegree[i]isfalse. - Return these vertices as
res.
Time & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of vertices and is the number of edges.
Common Pitfalls
Tracking Outgoing Edges Instead of Incoming
The problem asks for nodes that cannot be reached from other nodes, which means nodes with zero incoming edges. Tracking outgoing edges instead will identify nodes that cannot reach others, which is the opposite of what is needed.
Overcomplicating With Graph Traversal
Since the graph is acyclic and we only need nodes with no incoming edges, a simple pass through the edges is sufficient. Implementing full DFS or BFS adds unnecessary complexity and potential for bugs when a simple indegree check solves the problem optimally.
Sign in to join the discussion