2Brute2Best
🔥0

All Ancestors of a Node in a DAG

Medium·LeetCode #2192Basic Anchor
LeetCode
30-Second Mental ModelTopological Sort — DFS order on a DAG (forward reach from each node)
Complexity:O(N² · (N + E))→O(N · (N + E))
Key Insight

Instead of asking about pairs, run one search from each node i. Every node that search reaches has i as an ancestor, so add i to each of their lists. Start the searches in order 0, 1, … , n − 1, so every list fills up in sorted order.

Core Validity Invariant

Node i is an ancestor of w exactly when the search from i reaches w. So each list gets one entry for each such i, and no other entries.

01

Problem

The nodes of a directed acyclic graph are numbered from 0 to n − 1. You are given edges, where edges[i] = [from, to] is an arrow from from to to. The graph has no cycles.

An ancestor of node v is any node u that can reach v by following arrows, through any number of steps. Direct parents count, and so do parents of parents, and so on.

For every node i, return the list of its ancestors, sorted in ascending order.

Input

n, the number of nodes, and edges, a list of arrows [from, to].

Output

answer[i], the sorted list of all ancestors of node i. The list is empty if no node can reach i.

Example 1
Input: n = 8, edges = [[0,3],[0,4],[1,3],[2,4],[2,7],[3,5],[3,6],[3,7],[4,6]]
Output: [[],[],[],[0,1],[0,2],[0,1,3],[0,1,2,3,4],[0,1,2,3]]

Nodes 0, 1 and 2 have no arrows pointing at them. Node 3 has arrows from 0 and 1. Node 6 is reached from 3 and 4, and node 4 is reached from 0 and 2, so the ancestors of 6 are 0, 1, 2, 3 and 4.

Example 2
Input: n = 5, edges = [[0,1],[0,2],[0,3],[0,4],[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
Output: [[],[0],[0,1],[0,1,2],[0,1,2,3]]

Every smaller node points at every bigger node, so node i has the ancestors 0, 1, … up to i − 1.

Constraints
  • • 1 ≤ n ≤ 1000
  • • 0 ≤ edges.length ≤ min(2000, n · (n − 1) / 2)
  • • edges[i].length = 2
  • • 0 ≤ from, to ≤ n − 1
  • • from ≠ to
  • • There are no duplicate edges.
  • • The graph is directed and acyclic.
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    An ancestor is any node that can reach this one through a chain of arrows. Does a direct arrow count, and does a longer chain count?

  2. 2.

    If I already know the ancestors of node 3, what can I say about the nodes that node 3 points to?

  3. 3.

    Is it easier to ask who can reach each node, or what each node can reach? Could one answer give me the other?

  4. 4.

    With n = 1000 there are about half a million pairs. How much work would one search per pair cost?

  5. 5.

    If I process the start nodes 0, 1, 2, … in order, what do I know about each list as I add to it?

03

Brute force approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceFor each pair (i, j), search from i to see whether it reaches jO(N² · (N + E))O(N²)
OptimalFrom every node i, search forward once and add i to the list of every node it reachesO(N · (N + E))O(N²)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “ancestors”Every node that can reach this node through any number of arrows, not only the direct parents.
  • “directed acyclic graph”Arrows have one direction and never form a loop. So 'u reaches v' is a one-way fact, and no node is its own ancestor.
  • “sorted in ascending order”Process the start nodes in order, so each list is filled in sorted order and no sort is needed.
directed acyclic graph+reach through any number of arrows+one search from each node, pushing forward→Topological Sort — DFS order on a DAG (forward reach from each node)

This is the first question of the DFS order / DAG DP group. The graph has no loops, so 'i reaches w' is a fixed, one-way fact. The trick is to flip the question: instead of asking who can reach w (backwards, from many places), let each node push itself forward onto everything it reaches. Loud and Rich and Longest Increasing Path go one step further and reuse answers with memoised DFS.

10

Don't make this mistake

Counting only the direct arrows
✗ for (auto& e : edges) ans[e[1]].push_back(e[0]); // only direct parents
✓ // add i to every node that the search from i reaches

This is the pattern-confusion trap: an ancestor is anyone who can reach the node, not only its direct parents. On the dry-run graph, node 6 has direct parents 3 and 4 only, but its ancestors are 0, 1, 2, 3 and 4. Node 0 reaches 6 through 3 (0 → 3 → 6) and through 4 (0 → 4 → 6). Node 5 gets [3] instead of [0, 1, 3].

Merging the parents' lists without removing duplicates
✗ for (int p : parents[w]) { ans[w].insert(ans[w].end(), ans[p].begin(), ans[p].end()); ans[w].push_back(p); } // no dedup, no sort
✓ // forward search: each i is added once per list, in increasing order of i

Merging parents is a tempting shortcut. On the dry run, node 6 has parents 3 and 4. Merging gives [0, 1, 3] + [0, 2, 4] = [0, 1, 3, 0, 2, 4]. The 0 appears twice, and the list is not sorted. Removing duplicates and sorting would fix it, but the forward search never creates the problem.

Swapping the direction: descendants instead of ancestors
✗ // inside the search from i, when w is first reached: ans[i].push_back(w); // what i reaches
✓ // inside the search from i, when w is first reached: ans[w].push_back(i); // i is an ancestor of w

The wrong line fills the list of i with the nodes that i reaches, which are its descendants. On the dry run, node 0 would get [3, 4, 5, 6, 7] instead of []. Node 0 has no ancestors, since nothing points at it.

11

Want to implement this?

Write it yourself on LeetCode, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Topological Sort translate into production architectures and low-level memory mechanics.