Task Scheduler
Given task labels and a cooldown n that must separate two runs of the same task, return the minimum number of CPU intervals needed to finish them all.
Open on LeetCode ↗Intuition
Your first instinct is to simulate the clock: a max-heap of counts, pop up to n+1 distinct tasks each round, push back what still has work, tick, repeat. It passes, and it teaches you nothing, because it hides the fact that the answer was decided before the first tick. Look at only the most frequent task. If it appears m times, you are forced to lay it down m times with at least n slots between consecutive copies — that is a rigid skeleton of (m-1) rows, each n+1 wide, plus a final row holding however many tasks are tied at m. Every other task now has exactly one of two fates: it drops into an idle hole inside that skeleton, changing nothing, or the holes run out and it extends the line to the right, at which point the CPU never idles at all and the length is simply the number of tasks. So the answer is max(len(tasks), (m-1)*(n+1) + countOfTasksWithMaxFreq) — two lower bounds, and the larger one is always achievable. The invariant the greedy simulation is secretly maintaining is that the busiest task is scheduled as early as its cooldown permits, every single round.
Approach
Only the frequencies matter
The CPU may run tasks in any order, so the input array is not a sequence to preserve — it is a multiset. Count each label with a Counter in one pass. From that point on the labels themselves are irrelevant too; all that survives into the arithmetic is the maximum count and how many labels tie for it.
The busiest task lays down a fixed frame
If the maximum frequency is m, the m copies of that task cannot be packed tighter than one every n+1 slots. That gives (m-1) complete rows of width n+1. The final row holds the last copy of the busiest task and the last copy of every other task tied at frequency m, since those are forced into the same final position — hence the + countOfTasksWithMaxFreq rather than + 1.
Take the max against the task count
The frame contains (m-1)*n idle holes. Remaining tasks fill them column by column, and if they run out first the frame length is the answer. If instead there are more tasks than holes, every hole is filled and the extras append to the end with no idling anywhere, so the schedule is exactly len(tasks) long. Taking the maximum of the two expressions covers both regimes in one line, with no case analysis.
Solution & live demo
Edge cases
The frame collapses to (m-1)*1 + ties, which never exceeds len(tasks), so the max returns len(tasks) — correct, since nothing forces a gap.
m = 4, ties = 1, frame = 3*3 + 1 = 10, which exceeds len(tasks) = 4. The answer is 10, mostly idle time.
The final row holds all of them side by side, which is why countOfTasksWithMaxFreq is added rather than 1.
The holes fill up and overflow, so len(tasks) dominates and the CPU never idles once.