Compute build order from dependencies
Company: Uber
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Technical Screen
Quick Answer: This question evaluates understanding of graph algorithms and dependency resolution, specifically topological ordering and cycle detection, within the Coding & Algorithms domain and tests both conceptual understanding and practical application of graph traversal and ordering techniques.
Constraints
- 0 <= n <= 10^4
- 0 <= dependencies.length <= 10^5
- Each dependency is a pair [a, b] with 0 <= a, b < n and a != b
- Duplicate dependency pairs may appear and count as one edge
- Among packages that are simultaneously ready, build the smallest-label one first
- Return [] if and only if the dependency graph contains a cycle
Examples
Input: (4, [[1, 0], [2, 0], [3, 1], [3, 2]])
Expected Output: [0, 1, 2, 3]
Explanation: 0 builds first (no deps); then 1 and 2 (both depend only on 0, smaller label 1 first); finally 3 (depends on 1 and 2).
Input: (2, [[1, 0], [0, 1]])
Expected Output: []
Explanation: 0 depends on 1 and 1 depends on 0 — a 2-node cycle, so no valid build order exists.
Input: (3, [])
Expected Output: [0, 1, 2]
Explanation: No dependencies; all three packages are independent disconnected components, emitted in ascending label order.
Input: (1, [])
Expected Output: [0]
Explanation: A single package with no dependencies builds immediately.
Input: (0, [])
Expected Output: []
Explanation: There are no packages, so the (trivially valid) build order is empty.
Input: (4, [[1, 0], [1, 0], [2, 1]])
Expected Output: [0, 1, 2, 3]
Explanation: The duplicate edge [1,0] is collapsed to one. Order: 0, then 1, then 2; package 3 has no dependencies and is appended (smallest ready label whenever it becomes available).
Input: (6, [[1, 0], [2, 0], [3, 1], [4, 1], [5, 2]])
Expected Output: [0, 1, 2, 3, 4, 5]
Explanation: A wider DAG; the smallest-label tie-break yields the canonical ascending order that still respects every dependency.
Input: (3, [[0, 1], [1, 2], [2, 0]])
Expected Output: []
Explanation: 0 -> needs 1, 1 -> needs 2, 2 -> needs 0 forms a 3-node cycle, so the function returns an empty list.
Hints
- Model packages as nodes and each dependency [a, b] as a directed edge b -> a (b must come before a). The build order is a topological sort of this graph.
- Use Kahn's algorithm: repeatedly take a node with in-degree 0, append it to the order, and decrement the in-degree of its successors. Deduplicate edges first so duplicate pairs don't inflate in-degrees.
- For the deterministic 'smallest label first' rule, replace the plain queue with a min-heap of ready nodes. If at the end you have placed fewer than n nodes, some nodes were stuck in a cycle — return an empty list.
- The alternative DFS post-order approach pushes a node onto a stack after visiting all its dependencies, then reverses the stack; it detects cycles via a recursion/'in-progress' marker. Kahn's is often preferred here because in-degree tracking makes the smallest-label tie-break and cycle reporting straightforward without deep recursion.