Skip to content

Commit 8eba570

Browse files
committed
feat: add more question
1 parent 3ee1da9 commit 8eba570

File tree

7 files changed

+288
-1
lines changed

7 files changed

+288
-1
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"problem_name": "task_scheduler",
3+
"solution_class_name": "Solution",
4+
"problem_number": "621",
5+
"problem_title": "Task Scheduler",
6+
"difficulty": "Medium",
7+
"topics": "Array, Hash Table, Greedy, Sorting, Heap (Priority Queue), Counting",
8+
"tags": ["grind-75"],
9+
"readme_description": "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.\n\nReturn the **minimum** number of CPU intervals required to complete all tasks.",
10+
"readme_examples": [
11+
{
12+
"content": "```\nInput: tasks = [\"A\",\"A\",\"A\",\"B\",\"B\",\"B\"], n = 2\nOutput: 8\n```\n**Explanation:** A possible sequence is: A -> B -> idle -> A -> B -> idle -> A -> B.\n\nAfter 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."
13+
},
14+
{
15+
"content": "```\nInput: tasks = [\"A\",\"C\",\"A\",\"B\",\"D\",\"B\"], n = 1\nOutput: 6\n```\n**Explanation:** A possible sequence is: A -> B -> C -> D -> A -> B.\n\nWith a cooling interval of 1, you can repeat a task after just one other task."
16+
},
17+
{
18+
"content": "```\nInput: tasks = [\"A\",\"A\",\"A\", \"B\",\"B\",\"B\"], n = 3\nOutput: 10\n```\n**Explanation:** A possible sequence is: A -> B -> idle -> idle -> A -> B -> idle -> idle -> A -> B.\n\nThere 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."
19+
}
20+
],
21+
"readme_constraints": "- `1 <= tasks.length <= 10^4`\n- `tasks[i]` is an uppercase English letter.\n- `0 <= n <= 100`",
22+
"readme_additional": "",
23+
"solution_imports": "",
24+
"solution_methods": [
25+
{
26+
"name": "least_interval",
27+
"parameters": "tasks: list[str], n: int",
28+
"return_type": "int",
29+
"dummy_return": "0"
30+
}
31+
],
32+
"test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution",
33+
"test_class_name": "TaskScheduler",
34+
"test_helper_methods": [
35+
{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }
36+
],
37+
"test_methods": [
38+
{
39+
"name": "test_least_interval",
40+
"parametrize": "tasks, n, expected",
41+
"parametrize_typed": "tasks: list[str], n: int, expected: int",
42+
"test_cases": "[([\"A\", \"A\", \"A\", \"B\", \"B\", \"B\"], 2, 8), ([\"A\", \"C\", \"A\", \"B\", \"D\", \"B\"], 1, 6), ([\"A\", \"A\", \"A\", \"B\", \"B\", \"B\"], 3, 10)]",
43+
"body": "result = self.solution.least_interval(tasks, n)\nassert result == expected"
44+
}
45+
],
46+
"playground_imports": "from solution import Solution",
47+
"playground_test_case": "# Example test case\ntasks = [\\\"A\\\", \\\"A\\\", \\\"A\\\", \\\"B\\\", \\\"B\\\", \\\"B\\\"]\nn = 2\nexpected = 8",
48+
"playground_execution": "result = Solution().least_interval(tasks, n)\nresult",
49+
"playground_assertion": "assert result == expected"
50+
}

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PYTHON_VERSION = 3.13
2-
PROBLEM ?= evaluate_reverse_polish_notation
2+
PROBLEM ?= task_scheduler
33
FORCE ?= 0
44

55
sync_submodules:

leetcode/task_scheduler/README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Task Scheduler
2+
3+
**Difficulty:** Medium
4+
**Topics:** Array, Hash Table, Greedy, Sorting, Heap (Priority Queue), Counting
5+
**Tags:** grind-75
6+
7+
**LeetCode:** [Problem 621](https://leetcode.com/problems/task-scheduler/description/)
8+
9+
## Problem Description
10+
11+
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.
12+
13+
Return the **minimum** number of CPU intervals required to complete all tasks.
14+
15+
## Examples
16+
17+
### Example 1:
18+
19+
```
20+
Input: tasks = ["A","A","A","B","B","B"], n = 2
21+
Output: 8
22+
```
23+
24+
**Explanation:** A possible sequence is: A -> B -> idle -> A -> B -> idle -> A -> B.
25+
26+
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.
27+
28+
### Example 2:
29+
30+
```
31+
Input: tasks = ["A","C","A","B","D","B"], n = 1
32+
Output: 6
33+
```
34+
35+
**Explanation:** A possible sequence is: A -> B -> C -> D -> A -> B.
36+
37+
With a cooling interval of 1, you can repeat a task after just one other task.
38+
39+
### Example 3:
40+
41+
```
42+
Input: tasks = ["A","A","A", "B","B","B"], n = 3
43+
Output: 10
44+
```
45+
46+
**Explanation:** A possible sequence is: A -> B -> idle -> idle -> A -> B -> idle -> idle -> A -> B.
47+
48+
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.
49+
50+
## Constraints
51+
52+
- `1 <= tasks.length <= 10^4`
53+
- `tasks[i]` is an uppercase English letter.
54+
- `0 <= n <= 100`

leetcode/task_scheduler/__init__.py

Whitespace-only changes.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 1,
6+
"id": "imports",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"from solution import Solution"
11+
]
12+
},
13+
{
14+
"cell_type": "code",
15+
"execution_count": 2,
16+
"id": "setup",
17+
"metadata": {},
18+
"outputs": [],
19+
"source": [
20+
"# Example test case\n",
21+
"tasks = [\"A\", \"A\", \"A\", \"B\", \"B\", \"B\"]\n",
22+
"n = 2\n",
23+
"expected = 8"
24+
]
25+
},
26+
{
27+
"cell_type": "code",
28+
"execution_count": 3,
29+
"id": "execute",
30+
"metadata": {},
31+
"outputs": [
32+
{
33+
"name": "stdout",
34+
"output_type": "stream",
35+
"text": [
36+
"Counter({'A': 3, 'B': 3})\n",
37+
"[-3, -3]\n"
38+
]
39+
},
40+
{
41+
"data": {
42+
"text/plain": [
43+
"8"
44+
]
45+
},
46+
"execution_count": 3,
47+
"metadata": {},
48+
"output_type": "execute_result"
49+
}
50+
],
51+
"source": [
52+
"result = Solution().least_interval(tasks, n)\n",
53+
"result"
54+
]
55+
},
56+
{
57+
"cell_type": "code",
58+
"execution_count": 4,
59+
"id": "test",
60+
"metadata": {},
61+
"outputs": [],
62+
"source": [
63+
"assert result == expected"
64+
]
65+
}
66+
],
67+
"metadata": {
68+
"kernelspec": {
69+
"display_name": "leetcode-py-py3.13",
70+
"language": "python",
71+
"name": "python3"
72+
},
73+
"language_info": {
74+
"codemirror_mode": {
75+
"name": "ipython",
76+
"version": 3
77+
},
78+
"file_extension": ".py",
79+
"mimetype": "text/x-python",
80+
"name": "python",
81+
"nbconvert_exporter": "python",
82+
"pygments_lexer": "ipython3",
83+
"version": "3.13.7"
84+
}
85+
},
86+
"nbformat": 4,
87+
"nbformat_minor": 5
88+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import heapq
2+
from collections import Counter, deque
3+
4+
5+
class Solution:
6+
# Time: O(T * n + m log m) where T = len(tasks), worst case with many idle periods
7+
# Space: O(m) where m ≤ 26, so O(1)
8+
def least_interval(self, tasks: list[str], n: int) -> int:
9+
counts = Counter(tasks)
10+
max_heap = [-count for count in counts.values()]
11+
heapq.heapify(max_heap)
12+
13+
step_num = 0
14+
queue: deque[tuple[int, int]] = deque() # (count, available_time)
15+
16+
while max_heap or queue:
17+
step_num += 1
18+
19+
while queue and queue[0][1] <= step_num:
20+
count, _ = queue.popleft()
21+
heapq.heappush(max_heap, count)
22+
23+
if max_heap:
24+
count = heapq.heappop(max_heap)
25+
count += 1 # Decrease count (was negative)
26+
if count < 0: # Still has tasks left
27+
queue.append((count, step_num + n + 1))
28+
29+
return step_num
30+
31+
32+
class SolutionGreedy:
33+
# Time: O(T + m) where T = len(tasks), m = unique tasks ≤ 26, so O(T)
34+
# Space: O(m) where m ≤ 26, so O(1)
35+
def least_interval(self, tasks: list[str], n: int) -> int:
36+
"""
37+
Mathematical approach:
38+
39+
Key insight: The most frequent task determines the minimum time.
40+
41+
Example: tasks=["A","A","A","B","B","B"], n=2
42+
43+
1. Find max frequency: max_freq = 3 (A and B both appear 3 times)
44+
2. Count tasks with max frequency: max_count = 2 (A and B)
45+
3. Create frame structure:
46+
Frame: A B _ | A B _ | A B
47+
- (max_freq - 1) complete frames of size (n + 1)
48+
- Last frame contains only max frequency tasks
49+
50+
4. Calculate minimum intervals:
51+
- Frame intervals: (max_freq - 1) * (n + 1) = 2 * 3 = 6
52+
- Plus max frequency tasks: 6 + 2 = 8
53+
54+
5. Return max(total_tasks, calculated_min) to handle cases where
55+
we have enough variety to fill all gaps without idle time.
56+
"""
57+
counts = Counter(tasks)
58+
max_freq = max(counts.values())
59+
max_count = sum(1 for freq in counts.values() if freq == max_freq)
60+
61+
# Minimum intervals needed based on most frequent tasks
62+
min_intervals = (max_freq - 1) * (n + 1) + max_count
63+
64+
# Return max to handle cases with sufficient task variety
65+
return max(len(tasks), min_intervals)

leetcode/task_scheduler/tests.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import pytest
2+
3+
from leetcode_py.test_utils import logged_test
4+
5+
from .solution import Solution, SolutionGreedy
6+
7+
8+
class TestTaskScheduler:
9+
@pytest.mark.parametrize("solution_class", [Solution, SolutionGreedy])
10+
@pytest.mark.parametrize(
11+
"tasks, n, expected",
12+
[
13+
(["A", "A", "A", "B", "B", "B"], 2, 8),
14+
(["A", "C", "A", "B", "D", "B"], 1, 6),
15+
(["A", "A", "A", "B", "B", "B"], 3, 10),
16+
(["A", "A", "A", "A", "A", "A", "B", "C", "D", "E", "F", "G"], 2, 16),
17+
(["A"], 2, 1),
18+
],
19+
)
20+
@logged_test
21+
def test_least_interval(
22+
self,
23+
tasks: list[str],
24+
n: int,
25+
expected: int,
26+
solution_class: type[Solution | SolutionGreedy],
27+
):
28+
solution = solution_class()
29+
result = solution.least_interval(tasks, n)
30+
assert result == expected

0 commit comments

Comments
 (0)