Table of contents
Problem
You are given an array of CPU tasks
, each represented by letters A to Z, and a cooling time, n
. Each cycle or interval allows the completion of one task. Tasks can be completed in any order, but there's a constraint: identical tasks must be separated by at least n
intervals due to cooling time. (link)
Return the minimum number of intervals required to complete all tasks.
Example 1:
Input: tasks = ["A","A","A","B","B","B"], n = 2
Output: 8
Explanation: A possible sequence is: A -> B -> idle -> A -> B -> idle -> A -> B.
After completing task A, you must wait two cycles before doing A again. The same applies to task B. In the 3rd interval, neither A nor B can be done, so you idle. By the 4th cycle, you can do A again as 2 intervals have passed.
Example 2:
Input: tasks = ["A","C","A","B","D","B"], n = 1
Output: 6
Explanation: A possible sequence is: A -> B -> C -> D -> A -> B.
With a cooling interval of 1, you can repeat a task after just one other task.
Example 3:
Input: tasks = ["A","A","A", "B","B","B"], n = 3
Output: 10
Explanation: A possible sequence is: A -> B -> idle -> idle -> A -> B -> idle -> idle -> A -> B.
There are only two types of tasks, A and B, which need to be separated by 3 intervals. This leads to idling twice between repetitions of these tasks.
Constraints:
1 <= tasks.length <= 10^4
tasks[i]
is an uppercase English letter.0 <= n <= 100
Solution
Calculate Frequencies:
- First, determine the frequencies of all the letters.
Significance of ‘n’:
If task ‘A’ is currently running, it can only run again after
n
slots.The distance between consecutive ‘A’ tasks is
n
(excluding both).Visualize the problem as a set of slots with a size of
n+1
, referred to as a cycle.In each cycle, any letter can run only once.
If ‘A’ runs in the first cycle, it can only run again in the next cycle.
Goal:
The main goal is to minimize the number of cycles.
The minimum number of cycles required is determined by the highest frequency of any task.
Always start with the task that has the highest frequency.
For example, if the cycle length is
n+1
and there aren+1
single tasks with one task ‘A’ repeating twice:In the first
n+1
cycle, all single tasks are completed.In the second cycle, ‘A’ runs once, and in the third cycle, ‘A’ runs once again, totaling three cycles with
n
empty slots in the second and third cycles.
By executing the highest frequency task first, we ensure that subsequent cycles are efficiently utilized.
Slot Allocation:
Allocate slots based on frequency, starting with the highest and moving to the next highest in each cycle.
Use a priority queue to manage tasks:
In each cycle, remove the highest frequency task, place it in a slot, decrement its frequency by 1, and store it temporarily in list
Reinsert the updated frequencies into the priority queue for the next cycle.
Cycle Counting:
After each cycle, add
n+1
to the total steps completed.In the last cycle, it may not occupy all
n+1
slots. Use a counter (timer) to track the number of tasks executed.If the priority queue is empty, it indicates that the last cycle has just been completed.
Visualization
Example 1
Example 2
Example 3
class Solution {
private void addTheTempListBack(List<Integer> temp, PriorityQueue<Integer> pq){
for(int num: temp){
if(num!=0) pq.add(num);
}
}
public int leastInterval(char[] tasks, int n) {
Map<Character, Integer> map = new HashMap<>();
for(char task: tasks){
map.put(task, map.getOrDefault(task, 0)+1);
}
PriorityQueue<Integer> pq = new PriorityQueue<>((Integer a, Integer b)-> b-a);
pq.addAll(map.values());
int result = 0;
while(!pq.isEmpty()){
List<Integer> temp = new ArrayList<>();
int timer = 0; //Timer is only for the last cycle
for(int i=0; i<=n; i++){
if(pq.isEmpty()) break;
int top = pq.poll();
temp.add(top-1);
timer+=1;
}
addTheTempListBack(temp, pq);
result += pq.isEmpty() ? timer : n+1;
}
return result;
}
}