Task Scheduler

MediumGreedy · Counting🧩 GreedyLeetCode #621 ↗Updated

Problem

A CPU must run a list of tasks, each labelled 'A'..'Z' and taking one time unit. In every unit it either runs one task or sits idle, and two runs of the same label must be at least n units apart. Tasks may run in any order; return the minimum total number of units needed.
Input[A,A,A,B,B,B], n=2Output8 (AB_AB_AB)
Input[A,C,A,B,D,B], n=1Output6
Input[A,A,A,B,B,B], n=3Output10

1 ≤ tasks.length ≤ 104  ·  tasks[i] is an uppercase letter  ·  0 ≤ n ≤ 100

Try it yourself

Java
class Solution {
    public int leastInterval(char[] tasks, int n) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
The most frequent task (count maxF) forces a skeleton of maxF - 1 frames of length n + 1, plus a last frame holding every task that ties at maxF. Others fill the gaps; if they overflow, no idle is needed and the answer is just tasks.length.
PatternGreedy

Instinct An optimisation where one obvious local rule (earliest end, farthest reach, biggest first) never needs undoing - commit to it, then try hard to break it.

Memory hack Grab the best bite now and never look back - but only once a counterexample hunt fails to make you regret it.

Open the Greedy guide →

Approaches

Brute ForceTick-by-Tick Simulation

Time O(26·T), T = answerSpace O(1)

Advance time one unit at a time. Among tasks whose cooldown has expired, run the one with the most remaining copies (otherwise idle), and mark it unavailable until time + n + 1. Running the most frequent first keeps the longest chain from becoming the bottleneck.

Java
public int leastInterval(char[] tasks, int n) {
    int[] count = new int[26], nextFree = new int[26];
    for (char t : tasks) count[t - 'A']++;
    int left = tasks.length, time = 0;
    while (left > 0) {
        int best = -1;
        for (int c = 0; c < 26; c++) {
            if (count[c] > 0 && nextFree[c] <= time
                    && (best == -1 || count[c] > count[best])) best = c;
        }
        if (best != -1) {            // otherwise this unit is idle
            count[best]--;
            left--;
            nextFree[best] = time + n + 1;
        }
        time++;
    }
    return time;
}

Easy to trust, but walks through every idle unit one by one.

At a glance

LevelApproachTimeSpace
Brute ForceTick-by-Tick SimulationO(26·T), T = answerO(1)
BetterMax-Heap, One Frame of n+1 at a TimeO(N log k)O(k)
OptimalCounting FormulaO(N)O(1)
Common pitfalls
  • Dropping the max(…, tasks.length) — with many distinct tasks there are no idles.
  • Forgetting tied max-frequency tasks: the last frame holds maxCount tasks, not 1.
  • Counting a full n + 1 for the final heap frame — adds phantom idles.
Interview tips
  • Draw the frames for AAABBB, n=2: AB_ | AB_ | AB.
  • Explain why scheduling the most frequent task first is optimal (it is the bottleneck).
  • If asked for the actual schedule, use the heap version; for the length alone, the formula.
Pattern
Greedy by highest frequency + slot counting → scheduling with cooldowns or no-adjacent-duplicates.
My notes saved in this browser