Process Tasks Using Servers
Keep two heaps. The free pool holds servers that can take a task now, lightest on top. The busy pool holds servers that are still working, earliest finish on top. A busy server only needs to be checked when the clock reaches its finish time, so the busy pool can be a heap keyed by that time.
A server is in the free pool exactly when its freeTime ≤ t. The busy pool is checked only at finish times, so when we pick a server, the free pool is already correct for second t.
Problem
You have n servers and m tasks. Server i has a weight servers[i]. Task j takes tasks[j] seconds to run.
Task j joins a queue at second j, so task 0 joins at second 0, task 1 at second 1, and so on. The queue is served in order. Whenever the task at the front of the queue is waiting and a server is free, give the task to the free server with the smallest weight (ties go to the smaller index). If no server is free, the task waits until one is.
A server that starts a task at second t is busy until second t + tasks[j], and it is free again at that second. Several servers can become free in the same second.
Return ans, where ans[j] is the index of the server that runs task j.
servers, the weight of each server, and tasks, the seconds each task takes.
An array ans of length m: the server index for each task.
At second 0, task 0 goes to server 2 (weight 2, the lightest) and runs until second 1. At second 1, server 2 is free again and takes task 1 until second 3. At second 2, servers 0 and 1 are free and have the same weight, so the smaller index, server 0, takes task 2. The rest follows the same rule.
At second 0, task 0 goes to server 1 (weight 1) until second 2. At second 1, task 1 goes to server 4 (weight 2) until second 2. At second 2 both servers are free again, and task 2 takes server 1, the lighter one.
- • servers.length == n
- • tasks.length == m
- • 1 ≤ n, m ≤ 2 × 10^5
- • 1 ≤ servers[i], tasks[j] ≤ 2 × 10^5
First, think yourself
Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.
- 1.
Tasks wait in a queue. For one task, what decides which server it gets?
- 2.
If every server is busy, does the clock need to move one second at a time, or can it skip ahead?
- 3.
A server starts a task at second t and the task takes d seconds. At which second is that server free again?
- 4.
Can the clock ever go back to an earlier second when the next task is handled?
- 5.
If two servers finish in the same second, do I need to release both before I choose?
Brute force approach
Optimized approach
Why does this algorithm work?
Dry run
All approaches compared
| Approach | Idea | Time | Space |
|---|---|---|---|
| Brute Force | For each task, check all servers; if none is free, jump the clock to the earliest finish | O(M · N) | O(N) |
| Optimal | Free servers in a min-heap by (weight, index); busy servers in a min-heap by finish time | O((N + M) · log N) | O(N) |
Why does this belong to this pattern?
- “tasks join a queue, one per second”Tasks are served in order and time only moves forward, so the clock is a single pointer `t`.
- “smallest weight, then smallest index”The free servers form a min-heap keyed by (weight, index).
- “busy until t + tasks[j]”Busy servers form a second min-heap keyed by finish time, so the next server to come back is at the top.
The two pools are free (usable now) and busy (locked until its finish time). A server moves from busy back to free when the clock reaches its finish time, and it moves the other way when it takes a task, so items go back and forth. The neighbours in this sub-pattern differ: IPO moves projects one way only (locked to available), and Total Cost keeps two pools at the two ends of one list.
Don't make this mistake
C++ priority_queue is a max-heap by default. On the dry run the free pool holds (4, server 0) and (1, server 1). The max-heap pops server 0 first, so task 0 goes to the heavy server (weight 4) instead of the light one (weight 1). The busy pool needs the same fix: keyed by finish time, earliest first.
On the dry run, task 2 arrives at second 2, but both servers are busy until second 3. It really starts at 3 and runs until 4, not until 2 + 1 = 3. With the wrong line, server 1 looks free again at second 3 and takes task 3, so the answer becomes [1, 0, 1, 1, 1, 1] instead of [1, 0, 1, 0, 1, 1]. Stepping the clock one second at a time is also correct but slow: one server with 2 × 10^5 tasks of 2 × 10^5 seconds reaches second 4 × 10^10. Jump instead.
On the dry run, at second 4 servers 0 and 1 are both due, but the if releases only one (server 1). At second 5 task 5 releases server 0 (weight 4), and server 1 is still busy because its finish time is also 5 and only one server is released per task. Task 5 takes the heavy server, so the last answer is 0 instead of 1.
One server with 2 × 10^5 tasks of 2 × 10^5 seconds finishes at second 4 × 10^10, which overflows a 32-bit int.
Want to implement this?
Write it yourself on LeetCode, then come back and compare your code with the solutions above.
Open coding workspaceHow this concept connects to System Design & C++ Systems
Top engineering interviews test whether you understand how algorithms like Two Heaps translate into production architectures and low-level memory mechanics.