Build a Removable Timestamp-Ordered Task Scheduler
Company: ByteDance
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
## Problem
Implement a task scheduler with three operations:
```text
addTask(taskId, timestamp)
removeTask(taskId)
popTasks(count)
```
`addTask` inserts one task. `removeTask` removes that task if it is currently scheduled. `popTasks(count)` removes and returns up to `count` scheduled task IDs in ascending order of start timestamp. When timestamps tie, smaller task IDs come first.
Process a sequence of operations and return one array for every `pop` operation.
### Function Contract
Implement `runTaskScheduler(operations)`, where an operation is:
```text
["add", taskId, timestamp]
["remove", taskId]
["pop", count]
```
Return an array of task-ID arrays, one per `pop`, in operation order.
### Constraints & Assumptions
- `0 <= len(operations) <= 200,000`.
- `taskId` and `timestamp` are integers in the JavaScript-safe range `[-(2^53 - 1), 2^53 - 1]` so their values and ordering are preserved identically in all four supported languages.
- An `add` uses a task ID that is not currently scheduled. A previously removed or popped ID may be added again.
- Removing an absent task is a no-op.
- `count >= 0`; if fewer than `count` tasks remain, return and remove all of them.
- A `pop` with `count = 0` returns an empty array and changes no state.
### Clarifying Questions to Ask
- Does `pop` only inspect tasks or remove them? It removes every returned task.
- How are equal timestamps ordered? By increasing task ID.
- May removed task IDs be reused? Yes.
- What happens when `remove` names an absent task? Nothing.
```hint Combine ordering with membership
A min-heap gives the next timestamp, while a map or set records which task version is still active.
```
```hint Make reused IDs safe
If removal is lazy, attach a generation number to each heap entry so an old entry cannot remove a later task that reused the same ID.
```
### Example
```text
operations = [
["add", 7, 30],
["add", 2, 10],
["add", 5, 10],
["remove", 2],
["pop", 2],
["pop", 1]
]
```
Return `[[5, 7], []]`.
### Evaluation Focus
- Removes tasks returned by `pop` and never returns a removed task.
- Applies the timestamp-and-ID ordering deterministically.
- Handles stale heap entries and reuse of a task ID correctly.
- Preserves exact integer identity and ordering throughout the stated cross-language safe range.
- Runs each add or remove in `O(log n)` or better and returns `m` popped tasks in `O(m log n)` or better.
### Extensions to Discuss
1. How would an operation that changes a scheduled timestamp be supported?
2. What cleanup policy bounds stale heap entries after many removals?
3. How would the scheduler block until the earliest wall-clock timestamp becomes due?
Quick Answer: Build a task scheduler that supports insertion, removal, and bounded popping in timestamp order, using smaller task IDs to break equal-timestamp ties.
## Problem
Implement a task scheduler with three operations:
```text
addTask(taskId, timestamp)
removeTask(taskId)
popTasks(count)
```
`addTask` inserts one task. `removeTask` removes that task if it is currently scheduled. `popTasks(count)` removes and returns up to `count` scheduled task IDs in ascending order of start timestamp. When timestamps tie, smaller task IDs come first.
Process a sequence of operations and return one array for every `pop` operation.
### Function Contract
Implement `runTaskScheduler(operations)`, where an operation is:
```text
["add", taskId, timestamp]
["remove", taskId]
["pop", count]
```
Return an array of task-ID arrays, one per `pop`, in operation order.
### Constraints & Assumptions
- `0 <= len(operations) <= 200,000`.
- `taskId` and `timestamp` are integers in the JavaScript-safe range `[-(2^53 - 1), 2^53 - 1]` so their values and ordering are preserved identically in all four supported languages.
- An `add` uses a task ID that is not currently scheduled. A previously removed or popped ID may be added again.
- Removing an absent task is a no-op.
- `count >= 0`; if fewer than `count` tasks remain, return and remove all of them.
- A `pop` with `count = 0` returns an empty array and changes no state.
### Clarifying Questions to Ask
- Does `pop` only inspect tasks or remove them? It removes every returned task.
- How are equal timestamps ordered? By increasing task ID.
- May removed task IDs be reused? Yes.
- What happens when `remove` names an absent task? Nothing.
```hint Combine ordering with membership
A min-heap gives the next timestamp, while a map or set records which task version is still active.
```
```hint Make reused IDs safe
If removal is lazy, attach a generation number to each heap entry so an old entry cannot remove a later task that reused the same ID.
```
### Example
```text
operations = [
["add", 7, 30],
["add", 2, 10],
["add", 5, 10],
["remove", 2],
["pop", 2],
["pop", 1]
]
```
Return `[[5, 7], []]`.
### Evaluation Focus
- Removes tasks returned by `pop` and never returns a removed task.
- Applies the timestamp-and-ID ordering deterministically.
- Handles stale heap entries and reuse of a task ID correctly.
- Preserves exact integer identity and ordering throughout the stated cross-language safe range.
- Runs each add or remove in `O(log n)` or better and returns `m` popped tasks in `O(m log n)` or better.
### Extensions to Discuss
1. How would an operation that changes a scheduled timestamp be supported?
2. What cleanup policy bounds stale heap entries after many removals?
3. How would the scheduler block until the earliest wall-clock timestamp becomes due?
Constraints
- 0 <= len(operations) <= 200000.
- Each operation is [add, taskId, timestamp], [remove, taskId], or [pop, count].
- taskId and timestamp are integers in [-(2^53 - 1), 2^53 - 1], inclusive.
- An add uses an ID that is not currently scheduled; an ID may be reused after removal or pop.
- Removing an absent ID is a no-op.
- count >= 0; pop returns and removes at most count live IDs.
- Tasks are ordered by increasing timestamp, then increasing task ID.
Examples
Input: ([['add', 7, 30], ['add', 2, 10], ['add', 5, 10], ['remove', 2], ['pop', 2], ['pop', 1]],)
Expected Output: [[5, 7], []]
Explanation: Sample 1: task 2 is removed, then tasks 5 and 7 are popped by timestamp and ID.
Input: ([],)
Expected Output: []
Explanation: Sample 2: an empty operation stream produces no pop result arrays.
Hints
- Use a min-heap for timestamp-and-ID order and a separate map for current membership.
- Attach a new generation to every add so a stale entry for a reused ID cannot match the later live task.