Task Scheduler
Expert Answer & Key Takeaways
A complete guide to understanding and implementing Heap / Priority Queue.
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
Input: tasks = ["A","A","A","B","B","B"], n = 2
Output: 8
Input: tasks = ["A","A","A","B","B","B"], n = 0
Output: 6
Approach 1
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.
⏱ O(N * log 26) = O(N)💾 O(1) (only 26 uppercase letters)
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.
Approach 2
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.
⏱ O(N)💾 O(1)
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.
Approach 3
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⏱ O(N)💾 O(1) (26 tasks)
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.
Course4All Technical Board
Verified ExpertSenior Software Engineers & Algorithmic Experts
Our DSA content is authored and reviewed by engineers from top tech firms to ensure optimal time and space complexity analysis.
Pattern: 2026 Ready
Updated: Weekly
Found an issue or have a suggestion?
Help us improve! Report bugs or suggest new features on our Telegram group.