Task Scheduler
Master this topic with zero to advance depth.
Task Scheduler
Given a characters array tasks, representing the tasks a CPU needs to do, where each letter represents a different task. Tasks can be done in any order. Each task is done in one unit of time. For each unit of time, the CPU could complete either one task or just be idle.
However, there is a non-negative integer n that represents the cooldown period between two same tasks (the same letter in the array), that is that there must be at least n units of time between any two same tasks.
Return the least number of units of time that the CPU will take to finish all the given tasks.
Visual Representation
tasks = [A, A, A, B, B, B], n = 2
Simulation Steps:
1. A (freq 3->2) | Waitlist: [(A, T=3)]
2. B (freq 3->2) | Waitlist: [(A, T=3), (B, T=4)]
3. IDLE | Waitlist: [(A, T=3), (B, T=4)]
4. A (freq 2->1) | A returns from waitlist at T=3
...
Result: A B IDLE A B IDLE A B = 8 unitsExamples
Level I: Max-Heap + Cooling Queue (Simulation)
Intuition
We want to process the most frequent tasks first to minimize idle time. Use a Max-Heap to keep track of task frequencies and a Queue to manage tasks in their cooldown period.
Thought Process
- Count frequency of each task.
- Push frequencies into a Max-Heap.
- While heap or cooldown queue is not empty:
- Increment time.
- If heap has tasks, take the most frequent, decrement its count, and if count > 0, add to queue with its 'available time'.
- If queue has a task whose cooldown is over, push it back to the heap.
Detailed Dry Run
tasks = [A,A,A,B,B,B], n = 2
- Heap: [A:3, B:3], Queue: [], Time: 0
- Time 1: Process A. Heap [B:3], Queue [(A:2, T=4)]
- Time 2: Process B. Heap [], Queue [(A:2, T=4), (B:2, T=5)]
- Time 3: Idle. Queue is cool until T=4.
- Time 4: Q.pop A. Heap [A:2]... and so on until 8.
Level II: Greedy (Simplified Priority Queue)
Intuition
Always process the task with the highest remaining frequency. We use a single loop to calculate the size of chunks of size .
Thought Process
- Count frequencies.
- Use a Priority Queue (Max-Heap).
- In each chunk of time units, pick tasks from heap.
- If heap is empty but we still have tasks to process (temporarily removed), add 'idle' counts.
Detailed Dry Run
tasks = [A, A, A, B, B, B], n = 2
- Frequencies: {A:3, B:3}
- Iteration 1: Chunk size 3. Pick A (rem 2), B (rem 2). Total time 2. Remaining tasks exist, so add 1 idle. Time 3.
- Iteration 2: Pick A (rem 1), B (rem 1). Time 6.
- Iteration 3: Pick A (rem 0), B (rem 0). Time 8. Done.
Level III: Greedy (Mathematical)
Intuition
The total time is determined by the task with the maximum frequency. Let the maximum frequency be maxFreq. There are maxFreq - 1 gaps between these tasks, each of size n. We fill these gaps with other tasks. If the total units required is less than the total number of tasks, the answer is the total tasks.
Formula
partCount = maxFreq - 1
partLength = n - (countOfTasksWithMaxFreq - 1)
emptySlots = partCount * partLength
availableTasks = tasks.length - maxFreq * countOfTasksWithMaxFreq
idles = max(0, emptySlots - availableTasks)
Result = tasks.length + idles
Detailed Dry Run
tasks = [A,A,A,B,B,B], n = 2
- maxFreq = 3 (A and B both have 3).
- Max freq task count = 2 (A and B).
- Formula: (3-1) * (2+1) + 2 = 8. Total Time = 8.
Found an issue or have a suggestion?
Help us improve! Report bugs or suggest new features on our Telegram group.