Implement a Dependency-Aware Task Scheduler
Company: Scale AI
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: easy
Interview Round: Technical Screen
Quick Answer: This question evaluates skills in dependency management, deadline-driven scheduling and algorithmic data-structure design, focusing on handling directed acyclic graphs and ordering constraints.
Constraints
- 1 <= total number of tasks across all add operations <= 200000
- 0 <= total number of dependency links across all tasks <= 200000
- 0 <= ddl <= 10^9
- Task IDs are unique integers
- The dependency graph is acyclic and all prerequisite IDs are valid task IDs that may appear in earlier or later add operations
Examples
Input: [('add', [{'id': 1, 'ddl': 5, 'subTasks': []}, {'id': 2, 'ddl': 3, 'subTasks': []}]), ('consume',), ('consume',), ('consume',)]
Expected Output: [2, 1, 'NO_TASK']
Explanation: Tasks 1 and 2 are immediately executable. Task 2 has the earlier deadline, then task 1, then nothing remains.
Input: [('add', [{'id': 1, 'ddl': 5, 'subTasks': []}, {'id': 2, 'ddl': 1, 'subTasks': [1]}, {'id': 3, 'ddl': 4, 'subTasks': []}]), ('consume',), ('consume',), ('consume',), ('consume',)]
Expected Output: [3, 1, 2, 'NO_TASK']
Explanation: Task 2 has the smallest deadline but is blocked by task 1. So task 3 is consumed first, then 1, which unlocks 2.
Hints
- You need one structure to quickly get the executable task with the smallest deadline, and another structure to update tasks that become executable after a prerequisite is consumed.
- Track, for each task, how many prerequisites are still unfinished, and keep reverse edges from each prerequisite to the tasks that depend on it.