Minimum Number of Vertices to Reach All Nodes
You are given a directed graph with n nodes numbered 0 to n - 1 and a list of one-way edges, where [a, b] means you can travel from a to b. The graph has no cycles. Return the smallest set of starting nodes from which every node in the graph can be reached. You never have to explore anything. A node with no arrow pointing at it can only be reached by starting there, so it must be in the answer. Every other node already has something pointing at it, so it is covered. Collect the nodes with no incoming edges and you are done.
Constraints
- 2 ≤ n ≤ 105
- 1 ≤ edges.length ≤ min(105, n * (n - 1) / 2)
- Each edge is a pair [from, to] with from not equal to to
- All pairs are different and the graph has no cycles
Example
n = 6, edges = [[0, 1], [0, 2], [2, 5], [3, 4], [4, 2]][0, 3]Explanation Nodes 1, 2, 4 and 5 all have an arrow pointing at them, so something else can reach them. Nodes 0 and 3 have none, so they must both be starting points.
In plain terms
- In-degree
- How many arrows point at a node. A node with an in-degree of zero has no way in from anywhere else.
- Reachable
- A node you can arrive at by following arrows from some starting node.
- Acyclic
- Having no loops. Without loops, following arrows backwards from any node always ends at a node with no arrows in.
- Minimal set
- The smallest collection that still does the job. Here, dropping any node with in-degree zero would leave that node unreachable.
One cell per node, value = whether any arrow points at that node
Six nodes, and no edges read yet, so nothing has an arrow pointing at it.
What happens in this step
n = 6 edges = [[0, 1], [0, 2], [2, 5], [3, 4], [4, 2]] hasIncoming = no for every node Every node starts as a candidate for the answer.
Steps to visualize
- The row has one cell per node, labelled with the node number.
- The value is "no" while nothing points at that node and "yes" once something does.
- Read each edge and mark only its second node, because that is the end the arrow points at.
- The first node of an edge is ignored completely, which is why this runs in one quick pass.
- When all edges are read, every node still showing "no" belongs in the answer.
Walk through the code
Same walkthrough, now with the code. Press Next to move one step and watch which lines run.
Six nodes, and no edges read yet, so nothing has an arrow pointing at it.
What happens in this step
n = 6 edges = [[0, 1], [0, 2], [2, 5], [3, 4], [4, 2]] hasIncoming = no for every node Every node starts as a candidate for the answer.
Solution
function findSmallestSetOfVertices(n, edges) {
const hasIncoming = new Array(n).fill(false);
for (const edge of edges) {
hasIncoming[edge[1]] = true;
}
const result = [];
for (let node = 0; node < n; node++) {
if (!hasIncoming[node]) {
result.push(node);
}
}
return result;
}- Time
- O(n + e), where e is the number of edges
- Space
- O(n)
Test cases
| Input | Expected | Covers |
|---|---|---|
n = 6, edges = [[0, 1], [0, 2], [2, 5], [3, 4], [4, 2]] | [0, 3] | example from the docstring |
n = 5, edges = [[0, 1], [2, 1], [3, 1], [1, 4], [2, 4]] | [0, 2, 3] | several nodes pointing at the same target |
n = 3, edges = [] | [0, 1, 2] | no edges at all, so every node is its own starting point |
n = 1, edges = [] | [0] | the smallest graph there is |
n = 3, edges = [[0, 1], [1, 2]] | [0] | a chain where one start covers everything |
n = 4, edges = [[0, 1], [2, 3]] | [0, 2] | two separate pieces, each needing its own start |