Advertisement

All Ancestors of a Node in a Directed Acyclic Graph - LeetCode 2192 Solution

All Ancestors of a Node in a Directed Acyclic Graph - Complete Solution Guide

All Ancestors of a Node in a Directed Acyclic Graph is LeetCode problem 2192, a Medium level challenge. This complete guide provides step-by-step explanations, multiple solution approaches, and optimized code in python3, java, cpp, c.

Problem Statement

You are given a positive integer n representing the number of nodes of a Directed Acyclic Graph (DAG). The nodes are numbered from 0 to n - 1 ( inclusive ). You are also given a 2D integer array edges , where edges[i] = [from i , to i ] denotes that there is a unidirectional edge from from i to to i in the graph. Return a list answer , where answer[i] is the list of ancestors of the i th node, sorted in ascending order . A node u is an ancestor of another node v if u can reach v via a set of edg

Detailed Explanation

The problem asks us to find all the ancestors of each node in a directed acyclic graph (DAG). The input is the number of nodes 'n' and a list of edges 'edges', where each edge represents a directed connection from one node to another. The output should be a list of lists, where the i-th list contains all the ancestors of node i, sorted in ascending order. An ancestor of a node 'v' is any node 'u' that can reach 'v' through a path of edges.

Solution Approach

The provided code implements a Breadth-First Search (BFS) approach with topological sorting. It first builds an adjacency list representation of the graph and calculates the in-degree of each node (number of incoming edges). Then, it uses a queue to process nodes with an in-degree of 0 (source nodes). As it processes each node, it updates the ancestor sets of its neighbors, propagating the ancestors of the current node to its neighbors. Finally, it converts the ancestor sets to sorted lists and returns the result.

Step-by-Step Algorithm

  1. Step 1: Build the Adjacency List: Create an adjacency list `adj` to represent the graph, where `adj[u]` stores the list of nodes that `u` points to.
  2. Step 2: Calculate In-Degrees: Calculate the in-degree of each node and store it in the `in_degree` array.
  3. Step 3: Initialize Ancestor Sets: Create a list of sets `ancestors_sets`, where `ancestors_sets[i]` will store the set of ancestors of node `i`.
  4. Step 4: Initialize Queue: Add all nodes with an in-degree of 0 to a queue `queue`. These are the source nodes.
  5. Step 5: BFS Traversal: While the queue is not empty, dequeue a node `u`. For each neighbor `v` of `u`, add `u` to the ancestor set of `v`, and also add all the ancestors of `u` to the ancestor set of `v`. Decrement the in-degree of `v`. If the in-degree of `v` becomes 0, enqueue `v`.
  6. Step 6: Convert Sets to Sorted Lists: Convert each set of ancestors to a sorted list and store it in the `result` list.
  7. Step 7: Return Result: Return the `result` list.

Key Insights

  • Insight 1: The graph is a DAG, meaning there are no cycles. This allows us to use topological sorting or similar graph traversal techniques without worrying about infinite loops.
  • Insight 2: We can efficiently find ancestors using a variation of Breadth-First Search (BFS) or Depth-First Search (DFS) combined with tracking visited nodes (ancestors) for each node.
  • Insight 3: Using sets to store ancestors for each node helps avoid duplicate entries and automatically facilitates the 'sorted in ascending order' requirement when converting to a list.

Complexity Analysis

Time Complexity: O(n + e)

Space Complexity: O(n^2)

Topics

This problem involves: Depth-First Search, Breadth-First Search, Graph, Topological Sort.

Companies

Asked at: Palantir Technologies.