Skip to content

Commit 3072174

Browse files
committed
Single-Threaded CPU
1 parent 7c918a7 commit 3072174

File tree

2 files changed

+221
-0
lines changed

2 files changed

+221
-0
lines changed

1834.single_threaded_cpu/cpu.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# 1834. Single-Threaded CPU
2+
# Topics: 'Sorting', 'Array', 'Heap (Priority Queue)'
3+
# Level: 'Medium'
4+
5+
# 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 i​​​​​​th​​​​ task will be available to process at enqueueTimei and will take processingTimei to finish processing.
6+
7+
# You have a single-threaded CPU that can process at most one task at a time and will act in the following way:
8+
9+
# If the CPU is idle and there are no available tasks to process, the CPU remains idle.
10+
# 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.
11+
# Once a task is started, the CPU will process the entire task without stopping.
12+
# The CPU can finish a task then start a new one instantly.
13+
14+
# Return the order in which the CPU will process the tasks.
15+
16+
17+
18+
# Example 1:
19+
20+
# Input: tasks = [[1,2],[2,4],[3,2],[4,1]]
21+
# Output: [0,2,3,1]
22+
# Explanation: The events go as follows:
23+
# - At time = 1, task 0 is available to process. Available tasks = {0}.
24+
# - Also at time = 1, the idle CPU starts processing task 0. Available tasks = {}.
25+
# - At time = 2, task 1 is available to process. Available tasks = {1}.
26+
# - At time = 3, task 2 is available to process. Available tasks = {1, 2}.
27+
# - Also at time = 3, the CPU finishes task 0 and starts processing task 2 as it is the shortest. Available tasks = {1}.
28+
# - At time = 4, task 3 is available to process. Available tasks = {1, 3}.
29+
# - At time = 5, the CPU finishes task 2 and starts processing task 3 as it is the shortest. Available tasks = {1}.
30+
# - At time = 6, the CPU finishes task 3 and starts processing task 1. Available tasks = {}.
31+
# - At time = 10, the CPU finishes task 1 and becomes idle.
32+
33+
# Example 2:
34+
35+
# Input: tasks = [[7,10],[7,12],[7,5],[7,4],[7,2]]
36+
# Output: [4,3,2,0,1]
37+
# Explanation: The events go as follows:
38+
# - At time = 7, all the tasks become available. Available tasks = {0,1,2,3,4}.
39+
# - Also at time = 7, the idle CPU starts processing task 4. Available tasks = {0,1,2,3}.
40+
# - At time = 9, the CPU finishes task 4 and starts processing task 3. Available tasks = {0,1,2}.
41+
# - At time = 13, the CPU finishes task 3 and starts processing task 2. Available tasks = {0,1}.
42+
# - At time = 18, the CPU finishes task 2 and starts processing task 0. Available tasks = {1}.
43+
# - At time = 28, the CPU finishes task 0 and starts processing task 1. Available tasks = {}.
44+
# - At time = 40, the CPU finishes task 1 and becomes idle.
45+
46+
47+
48+
# Constraints:
49+
50+
# tasks.length == n
51+
# 1 <= n <= 105
52+
# 1 <= enqueueTimei, processingTimei <= 109
53+
54+
import heapq
55+
from typing import List
56+
57+
class Solution:
58+
def getOrder(self, tasks: List[List[int]]) -> List[int]:
59+
my_list =[]
60+
for i in range (len(tasks)):
61+
my_list.append(Task(i,tasks[i][0], tasks[i][1]))
62+
sorted_tasks = sorted(my_list, key=lambda p: p.enqueue, reverse=True)
63+
64+
min_heap = []
65+
cur_time = sorted_tasks[-1].enqueue
66+
processed = []
67+
while sorted_tasks or len(min_heap) > 0:
68+
while sorted_tasks and sorted_tasks[-1].enqueue <= cur_time:
69+
heapq.heappush(min_heap, sorted_tasks.pop())
70+
if len(min_heap) < 1:
71+
cur_time = sorted_tasks[-1].enqueue
72+
else:
73+
task = heapq.heappop(min_heap)
74+
cur_time = cur_time+task.processing
75+
processed.append(task.index)
76+
77+
return processed
78+
79+
class Task:
80+
def __init__(self, index, enqueue, processing):
81+
self.index = index
82+
self.enqueue = enqueue
83+
self.processing = processing
84+
85+
def __lt__(self, other):
86+
if self.processing == other.processing:
87+
return self.index < other.index
88+
return self.processing < other.processing
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import heapq
2+
from typing import List
3+
from cpu import Solution
4+
5+
# Test Suite
6+
def run_tests():
7+
sol = Solution()
8+
9+
# Test 1: Example 1 from problem
10+
tasks = [[1,2],[2,4],[3,2],[4,1]]
11+
result = sol.getOrder(tasks)
12+
expected = [0,2,3,1]
13+
assert result == expected, f"Test 1 failed: expected {expected}, got {result}"
14+
print("✓ Test 1 passed: Example 1")
15+
16+
# Test 2: Example 2 from problem
17+
tasks = [[7,10],[7,12],[7,5],[7,4],[7,2]]
18+
result = sol.getOrder(tasks)
19+
expected = [4,3,2,0,1]
20+
assert result == expected, f"Test 2 failed: expected {expected}, got {result}"
21+
print("✓ Test 2 passed: Example 2 (all tasks at same time)")
22+
23+
# Test 3: Single task
24+
tasks = [[1,5]]
25+
result = sol.getOrder(tasks)
26+
expected = [0]
27+
assert result == expected, f"Test 3 failed: expected {expected}, got {result}"
28+
print("✓ Test 3 passed: Single task")
29+
30+
# Test 4: Tasks with same processing time (tie-breaker by index)
31+
tasks = [[0,3],[0,3],[0,3]]
32+
result = sol.getOrder(tasks)
33+
expected = [0,1,2]
34+
assert result == expected, f"Test 4 failed: expected {expected}, got {result}"
35+
print("✓ Test 4 passed: Same processing time, sorted by index")
36+
37+
# Test 5: Sequential tasks (no overlap)
38+
tasks = [[1,2],[5,3],[10,1]]
39+
result = sol.getOrder(tasks)
40+
expected = [0,1,2]
41+
assert result == expected, f"Test 5 failed: expected {expected}, got {result}"
42+
print("✓ Test 5 passed: Sequential non-overlapping tasks")
43+
44+
# Test 6: Tasks with gaps (CPU idle periods)
45+
tasks = [[1,2],[10,3],[20,1]]
46+
result = sol.getOrder(tasks)
47+
expected = [0,1,2]
48+
assert result == expected, f"Test 6 failed: expected {expected}, got {result}"
49+
print("✓ Test 6 passed: Tasks with idle gaps")
50+
51+
# Test 7: All tasks start at time 0
52+
tasks = [[0,5],[0,3],[0,4]]
53+
result = sol.getOrder(tasks)
54+
expected = [1,2,0]
55+
assert result == expected, f"Test 7 failed: expected {expected}, got {result}"
56+
print("✓ Test 7 passed: All tasks at time 0")
57+
58+
# Test 8: Tasks arrive while processing
59+
tasks = [[0,10],[1,1],[2,1],[3,1]]
60+
result = sol.getOrder(tasks)
61+
expected = [0,1,2,3]
62+
assert result == expected, f"Test 8 failed: expected {expected}, got {result}"
63+
print("✓ Test 8 passed: Tasks arrive during long processing")
64+
65+
# Test 9: Later task has shorter processing time
66+
tasks = [[0,5],[1,2]]
67+
result = sol.getOrder(tasks)
68+
expected = [0,1]
69+
assert result == expected, f"Test 9 failed: expected {expected}, got {result}"
70+
print("✓ Test 9 passed: Later task can't interrupt")
71+
72+
# Test 10: Multiple tasks become available, choose shortest
73+
tasks = [[1,5],[2,2],[2,3],[2,1]]
74+
result = sol.getOrder(tasks)
75+
expected = [0,3,1,2]
76+
assert result == expected, f"Test 10 failed: expected {expected}, got {result}"
77+
print("✓ Test 10 passed: Choose shortest among available")
78+
79+
# Test 11: Large enqueue times (testing time jump)
80+
tasks = [[1000000000,1],[1000000001,1]]
81+
result = sol.getOrder(tasks)
82+
expected = [0,1]
83+
assert result == expected, f"Test 11 failed: expected {expected}, got {result}"
84+
print("✓ Test 11 passed: Large enqueue times")
85+
86+
# Test 12: Same enqueue and processing, different indices
87+
tasks = [[5,5],[5,5],[5,5]]
88+
result = sol.getOrder(tasks)
89+
expected = [0,1,2]
90+
assert result == expected, f"Test 12 failed: expected {expected}, got {result}"
91+
print("✓ Test 12 passed: Identical tasks, sorted by index")
92+
93+
# Test 13: Reverse order arrival
94+
tasks = [[10,1],[5,1],[1,1]]
95+
result = sol.getOrder(tasks)
96+
expected = [2,1,0]
97+
assert result == expected, f"Test 13 failed: expected {expected}, got {result}"
98+
print("✓ Test 13 passed: Tasks arrive in reverse order")
99+
100+
# Test 14: Mix of short and long tasks
101+
tasks = [[1,100],[2,1],[3,1],[4,1]]
102+
result = sol.getOrder(tasks)
103+
expected = [0,1,2,3]
104+
assert result == expected, f"Test 14 failed: expected {expected}, got {result}"
105+
print("✓ Test 14 passed: Long task followed by short tasks")
106+
107+
# Test 15: Tasks that pile up during processing
108+
tasks = [[0,50],[10,1],[20,1],[30,1],[40,1]]
109+
result = sol.getOrder(tasks)
110+
expected = [0,1,2,3,4]
111+
assert result == expected, f"Test 15 failed: expected {expected}, got {result}"
112+
print("✓ Test 15 passed: Tasks pile up during long processing")
113+
114+
# Test 16: Edge case - two tasks, second has same enqueue as first finishes
115+
tasks = [[1,2],[3,1]]
116+
result = sol.getOrder(tasks)
117+
expected = [0,1]
118+
assert result == expected, f"Test 16 failed: expected {expected}, got {result}"
119+
print("✓ Test 16 passed: Task arrives exactly when CPU becomes free")
120+
121+
# Test 17: Multiple tasks with complex tie-breaking
122+
tasks = [[0,5],[0,5],[0,3],[0,3]]
123+
result = sol.getOrder(tasks)
124+
expected = [2,3,0,1]
125+
assert result == expected, f"Test 17 failed: expected {expected}, got {result}"
126+
print("✓ Test 17 passed: Complex tie-breaking scenario")
127+
128+
print("\n" + "="*50)
129+
print("All 17 tests passed! ✓")
130+
print("="*50)
131+
132+
if __name__ == "__main__":
133+
run_tests()

0 commit comments

Comments
 (0)