Skip to main content
  1. LeetCode/

LeetCode 2192: All Ancestors of a Node in a DAG

·3 mins· ·
LeetCode Medium Graph Topological-Sort
Wei Yi Chung
Author
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
Table of Contents

Meta Data
#

Difficulty: medium First Attempt: 2026-04-08 Source: Day 2 learning note

Study Context
#

This article is rebuilt from the exact LeetCode section in the learning note. I kept the note’s repair points, comparison points, and common mistakes, while removing unrelated non-LeetCode material from the same day.

Learning Note Extract
#

LC 2192 - All Ancestors of a Node in a DAG
#

  • Pattern: Graph traversal - DFS from each source OR BFS with Kahn’s topological order and set propagation.
  • Key insight: Ancestors are transitive. If 0 -> 1 -> 3, then 0 is also an ancestor of 3.
  • DFS approach: For each src node, run DFS and add src to every reachable node’s ancestor list. Use a fresh visited set per DFS to prevent duplicates. Result is naturally sorted because src iterates in order.
  • Common bugs: Shared visited set across DFS calls, appending current node instead of original src, and appending before a visited check.
  • BFS approach: Use Kahn’s topological sort. For each edge node -> neighbor, propagate ancestors[node] plus node into ancestors[neighbor]. Topological order guarantees each node’s ancestors are computed before it propagates.
  • Why BFS is faster: DFS repeats traversal from many sources. Kahn propagation processes graph edges once, with set-union cost.
  • Time complexity (BFS): O(V^2 + E) for propagation in the worst case, plus O(V^2 log V) if sorting large ancestor lists.

Organized Notes
#

This article should stay on transitive ancestors in a DAG. The Kahn solution works because once a node is popped, every ancestor that can reach it through earlier nodes has already been accumulated. For each edge u -> v, add u and all of u’s ancestors into v’s set. Sorting happens only at the end, which keeps the propagation logic simple and avoids duplicate ancestor entries.

Clean Solution
#

The note above captures the reasoning and the mistakes to avoid. The implementation below is the version I would submit.

from collections import deque
from typing import List

class Solution:
    def getAncestors(self, n: int, edges: List[List[int]]) -> List[List[int]]:
        graph = [[] for _ in range(n)]
        indeg = [0] * n
        ancestors = [set() for _ in range(n)]

        for u, v in edges:
            graph[u].append(v)
            indeg[v] += 1

        q = deque(i for i in range(n) if indeg[i] == 0)
        while q:
            u = q.popleft()
            for v in graph[u]:
                ancestors[v].add(u)
                ancestors[v].update(ancestors[u])
                indeg[v] -= 1
                if indeg[v] == 0:
                    q.append(v)

        return [sorted(a) for a in ancestors]

Complexity
#

Time O(n^2 + e) in worst case, Space O(n^2).

Mistakes To Watch
#

  • Doing DFS from every node without controlling duplicate work.
  • Forgetting sorted output.

Final Interview Explanation
#

I would process the DAG in topological order and propagate ancestor sets forward. For every edge u -> v, u and all ancestors of u are ancestors of v. Topological order makes sure the set for u is complete before it is merged into v.

Related

LeetCode 207: Course Schedule
·2 mins
LeetCode Medium Graph Topological-Sort
LeetCode note for Course Schedule, rebuilt from the original learning note
LeetCode 210: Course Schedule II
·2 mins
LeetCode Medium Graph Topological-Sort
LeetCode note for Course Schedule II, rebuilt from the original learning note
LeetCode 1971: Find if Path Exists in Graph
·2 mins
LeetCode Easy Graph Union-Find
LeetCode note for Find if Path Exists in Graph, rebuilt from the original learning note
LeetCode 1497: Check If Array Pairs Are Divisible by k (Remainder Pairing, Modulo)
·2 mins
LeetCode Daily Medium Array Hash-Map Complement Counting Math Modulo
Remainder pairing with a hash map; handle 0 and k/2 remainders carefully.
LeetCode 1695: Maximum Erasure Value (Sliding Window)
·1 min
LeetCode Daily Medium Array Sliding-Window Two-Pointers Hash-Set
Sliding window with a set and running sum; two pointers to keep the subarray unique.
LeetCode 209: Minimum Size Subarray Sum (Sliding Window)
·1 min
LeetCode Daily Medium Array Sliding-Window Two-Pointers Prefix-Sum Binary-Search
Sliding window with two pointers; minimize subarray length where sum ≥ target.