AlgoMaster Logo

Task Scheduler

mediumFrequency8 min readUpdated June 23, 2026

Understanding the Problem

We have a set of CPU tasks and a cooldown constraint. After executing a task of type X, we must wait at least n intervals before executing another task of type X. During the wait, we can either execute a different task or sit idle.

The question is: what is the minimum total number of intervals (task executions + idle slots) needed to complete every task?

The task that appears most frequently is the bottleneck. If task A appears 6 times and n = 2, then we need at least 5 gaps of size (n+1) between consecutive A executions, plus the final A itself. Other tasks can fill those gaps and reduce idle time, but they cannot reduce the minimum imposed by the most frequent task.

The problem reduces to two questions: how many idle slots does the most frequent task force, and how many of those slots can other tasks fill?

Key Constraints:

  • 1 <= tasks.length <= 10^4 → An O(n log n) or O(n * 26) approach runs comfortably within limits.
  • tasks[i] is an uppercase English letter → At most 26 distinct task types, so sorting or scanning the 26 frequencies is effectively O(1).
  • 0 <= n <= 100 → When n = 0, the answer is the number of tasks. With few distinct tasks and a large cooldown, idle slots become common.

Approach 1: Simulation with Max-Heap

Intuition

Simulate the CPU step by step. At each time step, execute the available task with the highest remaining count. Delaying high-frequency tasks increases the chance of forced idle time later, because they need more gaps to spread out, so executing them as early as possible is the greedy choice.

A max-heap gives the most frequent available task in O(log 26) per step. The complication is cooldown: after a task executes, it cannot run again for the next n intervals, so it must leave the heap temporarily and return once its cooldown expires. A queue holds the cooling tasks, each paired with the time it becomes available.

At each time step, pop the most frequent task from the heap, decrement its count, and push it into the cooldown queue with availability time current + n + 1. Before popping, check whether the task at the front of the queue has become available, and if so move it back to the heap. If the heap is empty, the step is an idle interval.

Algorithm

  1. Count the frequency of each task
  2. Push all frequencies into a max-heap
  3. Initialize a time counter and a cooldown queue
  4. While the heap or queue is non-empty:
    • Increment time
    • If the front of the cooldown queue has an availability time equal to the current time, move that frequency back to the heap
    • If the heap is non-empty, pop the highest frequency, decrement it, and if it is still positive, add it to the cooldown queue with availability time = current time + n + 1
    • If the heap is empty (and we didn't move anything from the queue), this is an idle interval
  5. Return time

Example Walkthrough

1Init: Heap=[3,3], Queue=[], time=0
0
1
2
3
4
5
6
7
1/8

Code

This simulation processes one time step at a time, including every idle slot. The next approach processes a full round of n + 1 slots per iteration instead.

Approach 2: Greedy with Sorting (Round-Robin)

Intuition

Group the schedule into rounds of at most (n + 1) slots. A round is the smallest window in which any task can repeat: after executing a task, the next (n + 1) slots are exactly the cooldown window plus the slot the task occupied. Within one round, each distinct task can appear at most once, so filling a round with the (n + 1) most frequent remaining tasks never violates the constraint. When fewer than (n + 1) distinct tasks remain, the leftover slots are idle.

This maps to a table with (n + 1) columns, where each row is a round filled left to right from most frequent to least frequent. The number of rows equals the frequency of the most common task, since that task occupies one slot in every round until it runs out. The last row may be shorter because some tasks have been fully consumed.

Re-sorting after each round keeps the highest remaining frequencies at the end of the array. The work per round is O(1) because the array has fixed size 26.

Algorithm

  1. Count frequencies of each task
  2. Sort frequencies in ascending order (so the highest is at index 25)
  3. While the most frequent task still has remaining executions:
    • Process one round of at most (n + 1) tasks, picking from most frequent to least
    • Decrement each picked task's frequency
    • Add the number of slots used (tasks + idle) to the time counter
    • Re-sort the frequencies
  4. Return total time

Example Walkthrough

1Empty schedule. 3 rounds needed (maxFreq = 3), round size = n+1 = 3
0
1
2
3
4
5
6
7
1/5

Code

Both previous approaches loop through the schedule. The frequency distribution alone determines the answer, so the next approach computes it with a closed-form formula and no loop over rounds.

Approach 3: Math Formula (Optimal)

Intuition

Instead of simulating the schedule, derive the answer directly from the frequencies.

Consider the task with the highest frequency, maxFreq. It runs maxFreq times, and each consecutive pair of executions needs at least n intervals of cooldown between them. The most frequent task alone creates (maxFreq - 1) blocks, each of size (n + 1), followed by a final partial block.

If A appears 3 times and n = 2:

A _ _ | A _ _ | A

That is 2 blocks of 3 slots each, plus a final A. But what if multiple tasks share the maximum frequency? If both A and B appear 3 times:

A B _ | A B _ | A B

The final block now has 2 tasks instead of 1. If maxCount is the number of tasks with frequency equal to maxFreq, the formula becomes: result = (maxFreq - 1) * (n + 1) + maxCount.

There is one more case. When there are many distinct tasks and a small cooldown, the tasks themselves fill more than the formula predicts. So the final answer is: max(tasks.length, (maxFreq - 1) * (n + 1) + maxCount).

Algorithm

  1. Count the frequency of each task
  2. Find maxFreq, the highest frequency among all tasks
  3. Count maxCount, how many tasks have frequency equal to maxFreq
  4. Compute formulaResult = (maxFreq - 1) * (n + 1) + maxCount
  5. Return max(tasks.length, formulaResult)

Example Walkthrough

1Count frequencies: A=3, B=3. maxFreq=3, maxCount=2
0
A
1
A
2
A
3
B
4
B
5
B
1/5

Code