The Task Scheduler problem asks you to determine the minimum number of units of time that the CPU will take to finish all given tasks. Each task is represented by a capital letter (from 'A'
to 'Z'
), and each task takes exactly one unit of time to complete. However, the same task must be separated by at least n
units of time (the "cooldown period") before it can be executed again. During the cooldown period, the CPU can either execute a different task or be idle.
tasks
of characters representing the tasks to perform.n
representing the cooldown period between two same tasks.Constraints:
At first glance, it may seem that we need to simulate the entire scheduling process, placing tasks and idle slots as we go. This brute-force approach would involve at each step choosing the next available task that doesn't violate the cooldown constraint, and inserting idle slots when needed. However, this can be inefficient, especially with long task lists and larger cooldowns.
Upon closer inspection, we notice that the bottleneck is the task with the highest frequency. Since the same task must be separated by at least n
units, the most frequent task determines the minimal schedule length. The key insight is that instead of simulating every step, we can use a mathematical formula to compute the minimum time required based on the maximum task frequency and the cooldown.
The challenge is to fit all the other tasks and idle slots around the most frequent tasks as efficiently as possible. If there are enough different tasks to fill the cooldown slots, we can avoid idles altogether.
Let's break down the steps to solve the problem efficiently:
n
cooldown slots between them.(max_freq - 1) * (n + 1) + max_count
.Why use a hash map or array? Because we need to count task frequencies efficiently, and lookups in a hash map or array are O(1).
Example Input: tasks = ['A', 'A', 'A', 'B', 'B', 'B']
, n = 2
(3 - 1) * (2 + 1) + 2 = 2 * 3 + 2 = 6 + 2 = 8
Schedule could look like: A B idle A B idle A B (or any valid permutation). There are 8 time units in total.
The Task Scheduler problem can be solved efficiently by focusing on the task(s) with the highest frequency. By mathematically arranging these tasks with the required cooldowns, and filling in with other tasks as needed, we avoid unnecessary idle time. The elegant formula ensures we only use idle slots when absolutely necessary, and we can compute the answer in linear time with constant space. This approach leverages counting and simple arithmetic rather than brute-force simulation, making it both efficient and easy to understand.
from collections import Counter
class Solution:
def leastInterval(self, tasks, n):
freq = Counter(tasks)
max_freq = max(freq.values())
max_count = sum(1 for v in freq.values() if v == max_freq)
intervals = (max_freq - 1) * (n + 1) + max_count
return max(len(tasks), intervals)
#include <vector>
#include <algorithm>
class Solution {
public:
int leastInterval(std::vector<char>& tasks, int n) {
int freq[26] = {0};
for (char t : tasks) freq[t - 'A']++;
int max_freq = *std::max_element(freq, freq + 26);
int max_count = 0;
for (int i = 0; i < 26; ++i)
if (freq[i] == max_freq) max_count++;
int intervals = (max_freq - 1) * (n + 1) + max_count;
return std::max((int)tasks.size(), intervals);
}
};
import java.util.*;
class Solution {
public int leastInterval(char[] tasks, int n) {
int[] freq = new int[26];
for (char t : tasks) freq[t - 'A']++;
int maxFreq = 0, maxCount = 0;
for (int f : freq) maxFreq = Math.max(maxFreq, f);
for (int f : freq) if (f == maxFreq) maxCount++;
int intervals = (maxFreq - 1) * (n + 1) + maxCount;
return Math.max(tasks.length, intervals);
}
}
var leastInterval = function(tasks, n) {
let freq = Array(26).fill(0);
for (let t of tasks) freq[t.charCodeAt(0) - 65]++;
let maxFreq = Math.max(...freq);
let maxCount = freq.filter(f => f === maxFreq).length;
let intervals = (maxFreq - 1) * (n + 1) + maxCount;
return Math.max(tasks.length, intervals);
};