You are given n tasks labeled from 0 to n - 1 represented by a 2D integer array tasks, where tasks[i] = [enqueueTimei, processingTimei] means that the ith task will be available to process at enqueueTime[i] and will take processingTime[i] to finish processing.
You have a single-threaded CPU that can process at most one task at a time and will act in the following way:
If the CPU is idle and there are no available tasks to process, the CPU remains idle.
If the CPU is idle and there are available tasks, the CPU will choose the one with the shortest processing time. If multiple tasks have the same shortest processing time, it will choose the task with the smallest index.
Once a task is started to process, the CPU will process the entire task without stopping.
The CPU can finish a task then start a new one instantly.
Return the order in which the CPU will process the tasks.
Example 1:
Input: tasks = [[1,4],[3,3],[2,1]]
Output: [0,2,1]Example 2:
Input: tasks = [[5,2],[4,4],[4,1],[2,1],[3,3]]
Output: [3,4,2,0,1]Constraints:
1 <= tasks.length <= 100,0001 <= enqueueTime[i], processingTime[i] <= 1,000,000,000Before attempting this problem, you should be comfortable with:
A single-threaded CPU processes one task at a time. At any moment, we need to know which tasks are available (enqueue time has passed) and pick the one with the shortest processing time. A min-heap efficiently gives us the task with minimum processing time among available tasks. We use two heaps: one for pending tasks (sorted by enqueue time) and one for available tasks (sorted by processing time, then by index).
result, and advance time by its processing duration.result list.class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
available = []
pending = []
for i, (enqueueTime, processTime) in enumerate(tasks):
heapq.heappush(pending, (enqueueTime, processTime, i))
time = 0
res = []
while pending or available:
while pending and pending[0][0] <= time:
enqueueTime, processTime, i = heapq.heappop(pending)
heapq.heappush(available, (processTime, i))
if not available:
time = pending[0][0]
continue
processTime, i = heapq.heappop(available)
time += processTime
res.append(i)
return resInstead of using two heaps, we can sort the tasks by enqueue time first. This allows us to iterate through tasks in order and add them to the available heap as they become ready. We only need one heap for available tasks, simplifying the implementation while maintaining the same logic.
i to track which tasks have been considered.time, and record the index.class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
for i, t in enumerate(tasks):
t.append(i)
tasks.sort(key=lambda t: t[0])
res, minHeap = [], []
i, time = 0, tasks[0][0]
while minHeap or i < len(tasks):
while i < len(tasks) and time >= tasks[i][0]:
heapq.heappush(minHeap, [tasks[i][1], tasks[i][2]])
i += 1
if not minHeap:
time = tasks[i][0]
else:
procTime, index = heapq.heappop(minHeap)
time += procTime
res.append(index)
return resThis solution optimizes memory by not modifying the original tasks array. Instead of copying task data, we sort an array of indices by enqueue time. The heap stores only indices and uses the original tasks array for comparisons. This approach is more memory efficient while maintaining the same time complexity.
[0, 1, 2, ..., n-1] and sort it based on the tasks' enqueue times.time to 0, and create a min-heap that compares indices by their task's processing time (then by index for ties).time forward.time, and record the result.class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
n = len(tasks)
indices = list(range(n))
indices.sort(key=lambda i: (tasks[i][0], i))
class Task:
def __init__(self, idx):
self.idx = idx
def __lt__(self, other):
if tasks[self.idx][1] != tasks[other.idx][1]:
return tasks[self.idx][1] < tasks[other.idx][1]
return self.idx < other.idx
minHeap = []
res = []
time = i = 0
while minHeap or i < n:
while i < n and tasks[indices[i]][0] <= time:
heapq.heappush(minHeap, Task(indices[i]))
i += 1
if not minHeap:
time = tasks[indices[i]][0]
else:
next_task = heapq.heappop(minHeap)
time += tasks[next_task.idx][1]
res.append(next_task.idx)
return resAfter sorting tasks by enqueue time, the original indices get shuffled. Failing to preserve and correctly reference the original task indices leads to returning the wrong execution order. Always attach the original index to each task before any sorting operation.
The heap must prioritize by processing time first, then by original index for ties. Reversing this order or forgetting the tiebreaker causes incorrect task selection when multiple tasks have the same processing time.
When summing processing times, the cumulative time can exceed the 32-bit integer range for large inputs. Using a long or 64-bit integer for the time variable prevents overflow errors that lead to incorrect task availability checks.
When no tasks are available in the heap but tasks remain pending, the CPU must jump forward in time to the next task's enqueue time. Forgetting this case causes an infinite loop or incorrect scheduling when there are gaps between task arrivals.
Using a list and sorting it each iteration instead of a proper min-heap results in time complexity. The heap data structure is essential for efficiently selecting the minimum processing time task in time.