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:
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.
import heapq
from collections import Counter
def leastInterval(tasks, n):
# Count the frequency of each task
task_counts = Counter(tasks)
# Use a max heap to store the frequencies
max_heap = [-count for count in task_counts.values()]
heapq.heapify(max_heap)
time = 0
queue = [] # (available_time, task_count)
while max_heap or queue:
time += 1
if max_heap:
count = heapq.heappop(max_heap)
count += 1 # Decrement count (since it's negative)
if count != 0:
queue.append((time + n, count)) # Add to cooling queue
if queue and queue[0][0] == time:
heapq.heappush(max_heap, queue[0][1]) # Add back to available tasks
queue.pop(0)
return time
# Example usage:
tasks1 = ["A","A","A","B","B","B"]
n1 = 2
print(f"Example 1: tasks={tasks1}, n={n1}, result={leastInterval(tasks1, n1)}") # Output: 8
tasks2 = ["A","C","A","B","D","B"]
n2 = 1
print(f"Example 2: tasks={tasks2}, n={n2}, result={leastInterval(tasks2, n2)}") # Output: 6
tasks3 = ["A","A","A", "B","B","B"]
n3 = 3
print(f"Example 3: tasks={tasks3}, n={n3}, result={leastInterval(tasks3, n3)}") # Output: 10
Counter
to determine how many times each task appears.queue
is used to keep track of tasks that are in their cooling period. Each entry in the queue is a tuple of (available_time, task_count)
, where available_time
is the time when the task can be re-added to the max heap, and task_count
is the updated count of the task.def leastInterval_math(tasks, n):
task_counts = Counter(tasks)
max_count = max(task_counts.values())
max_count_tasks = sum(1 for count in task_counts.values() if count == max_count)
idle_slots = (max_count - 1) * (n + 1)
available_tasks = len(tasks) - max_count * max_count_tasks
idle_slots = max(0, idle_slots - available_tasks)
return len(tasks) + idle_slots
tasks1 = ["A","A","A","B","B","B"]
n1 = 2
print(f"Example 1 (Math): tasks={tasks1}, n={n1}, result={leastInterval_math(tasks1, n1)}") # Output: 8
tasks2 = ["A","C","A","B","D","B"]
n2 = 1
print(f"Example 2 (Math): tasks={tasks2}, n={n2}, result={leastInterval_math(tasks2, n2)}") # Output: 6
tasks3 = ["A","A","A", "B","B","B"]
n3 = 3
print(f"Example 3 (Math): tasks={tasks3}, n={n3}, result={leastInterval_math(tasks3, n3)}") # Output: 10
Counter
to count task frequencies.max_count
) and the number of tasks with this frequency (max_count_tasks
).(max_count - 1) * (n + 1)
. This is because each of the max_count - 1
occurrences of the most frequent task requires n
idle slots after it.available_tasks
), which are tasks that aren't the most frequent ones. available_tasks = len(tasks) - max_count * max_count_tasks
max(0, idle_slots - available_tasks)
. A negative value would mean there are enough other tasks to fill the idle slots.len(tasks) + idle_slots
.Counter
operation is O(N). The heap operations (push and pop) take O(log K) time, and in the worst case, we might perform these operations for each task. Thus, the overall time complexity is O(N log K).Counter
and the heap store at most K unique tasks.Counter
operation takes O(N) time. Finding the maximum count and the number of tasks with the maximum count also take O(N) time. Thus, the overall time complexity is O(N).Counter
stores at most K unique tasks.Empty Task List:
tasks
list is empty, the function should return 0.Zero Cooling Interval (n = 0):
n
is 0, the function should return the number of tasks, as there is no cooling period.Large Cooling Interval:
n
is very large compared to the number of tasks, the algorithm should correctly calculate the number of idle slots required.Tasks are all unique:
All Tasks are Same:
len(tasks) + (len(tasks) - 1) * n
.Mixed Task Types: