Detect cycle in directed dependency graph
Company: Uber
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates understanding of graph theory and algorithmic problem-solving, specifically cycle detection in directed dependency graphs and the ability to reason about combined edge sets across multiple dependency arrays.
Detect a Cycle in a Directed Dependency Graph
Constraints
- 1 <= n <= 10^5
- 0 <= a, b < n
- There may be zero or more edges.
- A self-loop [a, a] is considered a cycle.
Examples
Input: (3, [[0, 1], [1, 2], [2, 0]])
Expected Output: True
Explanation: 0 -> 1 -> 2 -> 0 forms a cycle.
Input: (3, [[0, 1], [1, 2]])
Expected Output: False
Explanation: A simple chain 0 -> 1 -> 2 has no cycle.
Input: (1, [])
Expected Output: False
Explanation: A single node with no edges has no cycle.
Input: (2, [[0, 1], [1, 0]])
Expected Output: True
Explanation: 0 -> 1 -> 0 is a 2-node cycle.
Input: (4, [[0, 1], [2, 3]])
Expected Output: False
Explanation: Two disjoint edges, no cycle in either component.
Input: (5, [[0, 1], [1, 2], [2, 3], [3, 4], [4, 1]])
Expected Output: True
Explanation: The back edge 4 -> 1 closes the loop 1 -> 2 -> 3 -> 4 -> 1.
Input: (1, [[0, 0]])
Expected Output: True
Explanation: A self-loop is a cycle.
Input: (6, [[0, 1], [0, 2], [1, 3], [2, 3], [3, 4], [4, 5]])
Expected Output: False
Explanation: A DAG: two paths converge at 3 then continue to 5, no back edge.
Hints
- A directed graph has a cycle if and only if a DFS encounters a 'back edge' — an edge that points to a node currently on the DFS recursion stack.
- Track three states per node: unvisited, currently-on-stack (gray), and fully-processed (black). Seeing a gray neighbor means a cycle.
- Because n can be up to 10^5, prefer an explicit (iterative) stack over deep recursion to avoid stack-overflow on long chains.
- Don't forget disconnected components: start a DFS from every node that is still unvisited.
Detect a Cycle in the Union of Multiple Dependency Arrays
Constraints
- 1 <= n <= 10^5
- 0 <= a, b < n
- Each array may have zero or more edges; there may be zero or more arrays.
- A cycle can span edges contributed by different arrays.
Examples
Input: (3, [[[0, 1]], [[1, 2]], [[2, 0]]])
Expected Output: True
Explanation: Three arrays each supply one edge; their union 0 -> 1 -> 2 -> 0 is a cycle.
Input: (3, [[[0, 1]], [[1, 2]]])
Expected Output: False
Explanation: Union is the chain 0 -> 1 -> 2, no cycle.
Input: (3, [[[0, 1], [1, 2]], [[2, 0]]])
Expected Output: True
Explanation: First array gives a path, second array closes it into a cycle.
Input: (4, [[[0, 1]], [[2, 3]]])
Expected Output: False
Explanation: Two disjoint edges across two arrays, no cycle.
Input: (2, [[], []])
Expected Output: False
Explanation: All arrays empty: no edges, no cycle.
Input: (1, [[[0, 0]]])
Expected Output: True
Explanation: A self-loop in one array is a cycle.
Input: (5, [[[0, 1], [1, 2]], [[2, 3], [3, 4]], [[4, 0]]])
Expected Output: True
Explanation: Edges spread across three arrays union into 0 -> 1 -> 2 -> 3 -> 4 -> 0.
Input: (4, [[[0, 1]], [[1, 2]], [[2, 3]]])
Expected Output: False
Explanation: Union is the chain 0 -> 1 -> 2 -> 3, no cycle.
Hints
- You do not need to detect a cycle per array. Merge every edge from every array into one shared adjacency list, then run a single cycle-detection pass.
- Reuse the same 3-color DFS from the single-graph version — the only change is how you populate the adjacency list.
- Watch for cycles that only appear after merging: array 1 gives a -> b and array 2 gives b -> a; neither alone has a cycle, the union does.
- Total work is O(n + E) where E is the combined edge count; iterating over arrays first does not change the asymptotic cost.