You are given an array of CPU tasks
, each labeled with a letter from A to Z, and a number n
. Each CPU interval can be idle or allow the completion of one task. Tasks can be completed in any order, but there's a constraint: there has to be a gap of at least n
intervals between two tasks with the same label.
Return the minimum number of CPU 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 intervals 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 interval, 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 <= 104
tasks[i]
is an uppercase English letter.0 <= n <= 100
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to the task scheduler problem involves trying every single possible order of tasks. We essentially simulate running the tasks in each possible order and calculating the total time it takes. This ensures we eventually find the fastest arrangement, but it's also very inefficient.
Here's how the algorithm would work step-by-step:
import itertools
def task_scheduler_brute_force(tasks, cooldown_period):
shortest_time = float('inf')
# Generate all possible task permutations
for task_permutation in itertools.permutations(tasks):
current_time = 0
last_executed = {}
for task in task_permutation:
# Check if cooldown is needed
if task in last_executed and \
current_time - last_executed[task] <= cooldown_period:
current_time = last_executed[task] + cooldown_period + 1
last_executed[task] = current_time
current_time += 1
# Update shortest time if needed
shortest_time = min(shortest_time, current_time)
return shortest_time
The most efficient strategy focuses on the most frequent task and arranges the others around it. We calculate the minimum time required based on how often the most frequent task needs to be spaced out, then fit in the remaining tasks where possible. This avoids simulating every possible task arrangement.
Here's how the algorithm would work step-by-step:
def least_interval(tasks, cool_down_interval):
task_counts = {}
for task in tasks:
task_counts[task] = task_counts.get(task, 0) + 1
max_frequency = 0
for task in task_counts:
max_frequency = max(max_frequency, task_counts[task])
# Calculate idle slots needed for the most frequent task.
idle_slots = (max_frequency - 1) * (cool_down_interval + 1)
for task in task_counts:
if task_counts[task] == max_frequency:
idle_slots -= 1
idle_slots = max(0, idle_slots)
# If idle_slots is positive, add it to the total tasks.
return len(tasks) + idle_slots
Case | How to Handle |
---|---|
Null or empty task array | Return 0 since there are no tasks to schedule. |
n is 0, meaning no cooldown is required | Return the length of the tasks array directly. |
Array contains only one type of task | The result will be (count - 1) * (n + 1) + count. |
n is a very large number compared to task array size | The number of idle slots calculation needs to ensure it does not result in negative values or overflow. |
Tasks are already optimally distributed (e.g., ['A', 'B', 'C', 'A', 'B', 'C'] with n=2) | The algorithm should handle cases without additional idle time needed. |
Integer overflow when calculating the total time | Use long data types for intermediate calculations where necessary. |
Very large input array size for tasks | The solution's space complexity (frequency map) should be efficient enough for a large number of tasks. |
Tasks are mostly unique, but still requires cooldowns | Ensure even relatively low-frequency tasks are still processed with the proper cooldown time. |