Compute build order from dependencies
Company: Veeva Systems
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Onsite
Quick Answer: Compute build order from dependencies evaluates algorithm design, data structures, correctness, complexity, edge cases, and implementation details in a realistic interview setting. A strong answer states assumptions, handles edge cases, explains trade-offs, and shows how to validate the result clearly.
Constraints
- Only nodes reachable from `start` (by following dependency edges) are included in the output.
- Node labels are strings; alphabetical order is standard string comparison.
- A node not appearing as a key in `graph` is treated as having no dependents (empty adjacency).
- When multiple tasks have all prerequisites met at the same time, the alphabetically smallest is emitted first.
- If the reachable subgraph contains a cycle, no valid order exists and the function returns "CYCLE".
Examples
Input: ({'A': ['B', 'C'], 'B': ['D', 'E'], 'C': ['D']}, 'A')
Expected Output: ['A', 'B', 'C', 'D', 'E']
Explanation: The prompt example. A has no prerequisites, so it goes first. B and C both depend only on A; alphabetically B precedes C. D depends on both B and C, so it comes after both; E depends on B. The deterministic order is A, B, C, D, E.
Input: ({'A': ['C', 'B'], 'B': ['D'], 'C': ['D'], 'D': []}, 'A')
Expected Output: ['A', 'B', 'C', 'D']
Explanation: Even though A lists C before B in its adjacency, both become available together after A, and the alphabetical tie-break emits B before C. D waits for both B and C.
Input: ({'A': ['B'], 'B': ['C'], 'C': ['A']}, 'A')
Expected Output: CYCLE
Explanation: A -> B -> C -> A forms a 3-node cycle; no node ever reaches in-degree 0 after the first pass, so no valid order exists and the function reports a cycle.
Input: ({'A': []}, 'A')
Expected Output: ['A']
Explanation: Single isolated node reachable from itself; the only valid order is [A].
Input: ({'A': ['B', 'C', 'D'], 'B': [], 'C': [], 'D': []}, 'A')
Expected Output: ['A', 'B', 'C', 'D']
Explanation: Star graph: B, C, D all depend solely on A and become available together; alphabetical tie-breaking emits them in order B, C, D.
Input: ({'X': ['Y', 'Z'], 'Y': ['W'], 'Z': ['W'], 'W': ['Q'], 'Q': []}, 'X')
Expected Output: ['X', 'Y', 'Z', 'W', 'Q']
Explanation: A diamond (Y and Z both feed W) followed by a chain. W waits for both Y and Z; Q waits for W. Non-A-prefixed labels confirm the algorithm is label-agnostic.
Input: ({'A': ['B'], 'B': ['A']}, 'A')
Expected Output: CYCLE
Explanation: Minimal 2-node cycle A <-> B; neither node can ever have in-degree 0, so a cycle is reported.
Hints
- First restrict the problem to the subgraph reachable from `start` via DFS/BFS — unreachable tasks should never appear in the output.
- Use Kahn's algorithm (repeatedly remove nodes with in-degree 0). To break ties alphabetically, store the currently-available nodes in a min-heap instead of a plain FIFO queue.
- Detect cycles by counting emitted nodes: if you process fewer nodes than exist in the reachable subgraph, the remaining nodes form (or feed into) a cycle.