Task scheduler with cooldown
Greedily run the most frequent remaining task each tick, using a max-heap of task counts and a queue for tasks that are cooling down.
Watch it animatedStep through it, with the running line highlighted. Free with an account.The idea
Greedily run whichever remaining task has the highest count, then let it cool down for n ticks before it can run again. A max-heap ordered by remaining count always surfaces the right task to run; a cooldown queue holds tasks that are temporarily unavailable until they rejoin the heap. If nothing is available on a tick, the CPU idles.
Code
public static int LeastInterval(char[] tasks, int n) {
var freq = new Dictionary<char, int>();
foreach (char t in tasks) freq[t] = freq.GetValueOrDefault(t) + 1;
var heap = new PriorityQueue<char, int>(Comparer<int>.Create((a, b) => b - a));
foreach (var kv in freq) heap.Enqueue(kv.Key, kv.Value);
var cooldown = new Queue<(char task, int count, int readyAt)>();
int time = 0;
while (heap.Count > 0 || cooldown.Count > 0) {
time++;
while (cooldown.Count > 0 && cooldown.Peek().readyAt <= time) {
var ready = cooldown.Dequeue();
heap.Enqueue(ready.task, ready.count);
}
if (heap.TryDequeue(out char t, out int cnt)) {
if (--cnt > 0) cooldown.Enqueue((t, cnt, time + n + 1));
}
}
return time;
}Tips
- If no task is available this tick because everyone is cooling down, the CPU sits idle — that idle time is part of the answer.
- The total time is just a counter that advances by one tick whether a task runs or the CPU idles.
In the .NET library
This greedy-plus-cooldown shape is the same one behind rate limiting: serve the highest-priority ready request, and anything over its limit waits in a queue until its window reopens.