Skip to content

Commit a1c2bd9

Browse files
committed
feat: check solution with old version
1 parent 8fc2d76 commit a1c2bd9

File tree

9 files changed

+253
-32
lines changed

9 files changed

+253
-32
lines changed

leetcode/lru_cache/playground.ipynb

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"cells": [
33
{
44
"cell_type": "code",
5-
"execution_count": null,
5+
"execution_count": 1,
66
"id": "imports",
77
"metadata": {},
88
"outputs": [],
@@ -13,7 +13,7 @@
1313
},
1414
{
1515
"cell_type": "code",
16-
"execution_count": null,
16+
"execution_count": 2,
1717
"id": "setup",
1818
"metadata": {},
1919
"outputs": [],
@@ -26,22 +26,51 @@
2626
},
2727
{
2828
"cell_type": "code",
29-
"execution_count": null,
29+
"execution_count": 5,
3030
"id": "run",
3131
"metadata": {},
32-
"outputs": [],
32+
"outputs": [
33+
{
34+
"name": "stdout",
35+
"output_type": "stream",
36+
"text": [
37+
"[None, None, None, 1, None, -1, None, -1, 3, 4]\n"
38+
]
39+
},
40+
{
41+
"data": {
42+
"text/plain": [
43+
"OrderedDict([(3, 3), (4, 4)])"
44+
]
45+
},
46+
"execution_count": 5,
47+
"metadata": {},
48+
"output_type": "execute_result"
49+
}
50+
],
3351
"source": [
3452
"result, cache = run_lru_cache(LRUCache, operations, inputs)\n",
3553
"print(result)\n",
36-
"cache"
54+
"cache.cache"
3755
]
3856
},
3957
{
4058
"cell_type": "code",
41-
"execution_count": null,
59+
"execution_count": 4,
4260
"id": "assert",
4361
"metadata": {},
44-
"outputs": [],
62+
"outputs": [
63+
{
64+
"data": {
65+
"text/plain": [
66+
"True"
67+
]
68+
},
69+
"execution_count": 4,
70+
"metadata": {},
71+
"output_type": "execute_result"
72+
}
73+
],
4574
"source": [
4675
"assert_lru_cache(result, expected)"
4776
]
@@ -61,7 +90,8 @@
6190
"file_extension": ".py",
6291
"mimetype": "text/x-python",
6392
"name": "python",
64-
"nbconvert_exporter": "python3",
93+
"nbconvert_exporter": "python",
94+
"pygments_lexer": "ipython3",
6595
"version": "3.13.7"
6696
}
6797
},

leetcode/maximum_profit_in_job_scheduling/solution.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,31 @@ def job_scheduling(self, start_time: list[int], end_time: list[int], profit: lis
2020
dp[i] = max(take, skip)
2121

2222
return dp[-1] if jobs else 0
23+
24+
25+
# bisect and insort Explanation:
26+
#
27+
# Etymology: "bisect" = bi (two) + sect (cut) = cut into two parts
28+
# Bisection method = binary search algorithm that repeatedly cuts search space in half
29+
#
30+
# bisect module provides binary search for SORTED lists (O(log n)):
31+
# - bisect_left(arr, x): leftmost insertion position
32+
# - bisect_right(arr, x): rightmost insertion position (default)
33+
# - bisect(arr, x): alias for bisect_right
34+
#
35+
# insort module maintains sorted order while inserting:
36+
# - insort_left(arr, x): insert at leftmost position
37+
# - insort_right(arr, x): insert at rightmost position (default)
38+
# - insort(arr, x): alias for insort_right
39+
#
40+
# Examples:
41+
# arr = [1, 3, 3, 5]
42+
# bisect_left(arr, 3) → 1 (before existing 3s)
43+
# bisect_right(arr, 3) → 3 (after existing 3s)
44+
# bisect_right(arr, 4) → 3 (between 3 and 5)
45+
#
46+
# insort(arr, 4) → arr becomes [1, 3, 3, 4, 5]
47+
#
48+
# In our solution:
49+
# bisect_right([2,4,6], 5) = 2 (insertion position)
50+
# j = 2 - 1 = 1 (index of latest job ending ≤ start_time)

leetcode/partition_equal_subset_sum/solution.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,23 @@ class Solution:
33
# Time: O(n * sum)
44
# Space: O(sum)
55
def can_partition(self, nums: list[int]) -> bool:
6+
"""
7+
Example: nums = [1, 5, 11, 5], target = 11
8+
9+
Initial: dp = [T, F, F, F, F, F, F, F, F, F, F, F]
10+
0 1 2 3 4 5 6 7 8 9 10 11
11+
12+
After num=1: [T, T, F, F, F, F, F, F, F, F, F, F]
13+
└─┘ (can make sum 1)
14+
15+
After num=5: [T, T, F, F, F, T, T, F, F, F, F, F]
16+
└─┘ └─┘ └─┘ (can make sums 5,6)
17+
18+
After num=11:[T, T, F, F, F, T, T, F, F, F, F, T]
19+
└─┘ (target!)
20+
21+
Backward iteration prevents using same number twice
22+
"""
623
total = sum(nums)
724
if total % 2:
825
return False
@@ -20,3 +37,57 @@ def can_partition(self, nums: list[int]) -> bool:
2037
return True
2138

2239
return False
40+
41+
42+
class SolutionBitset:
43+
# Time: O(n * sum)
44+
# Space: O(1)
45+
def can_partition(self, nums: list[int]) -> bool:
46+
"""
47+
Example: nums = [1, 5, 11, 5], target = 11
48+
49+
Bitset representation (bit position = achievable sum):
50+
51+
Initial: dp = 1 (binary: 1)
52+
Bits: ...0001
53+
Sums: {0}
54+
55+
After num=1: dp |= dp << 1
56+
a = dp = 1 (bin: 0001)
57+
b = dp << 1 = 2 (bin: 0010)
58+
c = a | b = 3 (bin: 0011)
59+
Sums: {0, 1}
60+
61+
After num=5: dp |= dp << 5
62+
a = dp = 3 (bin: 0000011)
63+
b = dp << 5 = 96 (bin: 1100000)
64+
c = a | b = 99 (bin: 1100011)
65+
Sums: {0, 1, 5, 6}
66+
67+
After num=11: dp |= dp << 11
68+
a = dp = 99 (bin: 00000001100011)
69+
b = dp << 11 = 202752 (bin: 110001100000000)
70+
c = a | b = 202851 (bin: 110001101100011)
71+
Sums: {0, 1, 5, 6, 11, 12, 16, 17}
72+
73+
Check: (dp & (1 << 11)) != 0
74+
a = dp = 202851 (bin: 110001101100011)
75+
b = 1 << 11 = 2048 (bin: 100000000000)
76+
c = a & b = 2048 (bin: 100000000000)
77+
c != 0 → bit 11 is set → True!
78+
"""
79+
total = sum(nums)
80+
if total % 2 != 0:
81+
return False
82+
83+
target = total // 2
84+
dp = 1
85+
86+
for num in nums:
87+
dp |= dp << num
88+
89+
# Early termination: found target sum!
90+
if (dp & (1 << target)) != 0:
91+
return True
92+
93+
return False

leetcode/partition_equal_subset_sum/test_solution.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
from leetcode_py.test_utils import logged_test
44

55
from .helpers import assert_can_partition, run_can_partition
6-
from .solution import Solution
6+
from .solution import Solution, SolutionBitset
77

88

99
class TestPartitionEqualSubsetSum:
10-
def setup_method(self):
11-
self.solution = Solution()
1210

1311
@logged_test
12+
@pytest.mark.parametrize("solution_class", [Solution, SolutionBitset])
1413
@pytest.mark.parametrize(
1514
"nums, expected",
1615
[
@@ -23,6 +22,6 @@ def setup_method(self):
2322
([1, 2, 5], False),
2423
],
2524
)
26-
def test_can_partition(self, nums: list[int], expected: bool):
27-
result = run_can_partition(Solution, nums)
25+
def test_can_partition(self, nums: list[int], expected: bool, solution_class: type):
26+
result = run_can_partition(solution_class, nums)
2827
assert_can_partition(result, expected)

leetcode/task_scheduler/solution.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,38 @@
1+
import heapq
2+
from collections import Counter, deque
3+
4+
15
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)
215

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:
333
# Time: O(T + m) where T = len(tasks), m = unique tasks ≤ 26, so O(T)
434
# Space: O(m) where m ≤ 26, so O(1)
535
def least_interval(self, tasks: list[str], n: int) -> int:
6-
from collections import Counter
7-
836
"""
937
Mathematical approach:
1038

leetcode/task_scheduler/test_solution.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
from leetcode_py.test_utils import logged_test
44

55
from .helpers import assert_least_interval, run_least_interval
6-
from .solution import Solution
6+
from .solution import Solution, SolutionGreedy
77

88

99
class TestTaskScheduler:
10-
def setup_method(self):
11-
self.solution = Solution()
1210

1311
@logged_test
12+
@pytest.mark.parametrize("solution_class", [Solution, SolutionGreedy])
1413
@pytest.mark.parametrize(
1514
"tasks, n, expected",
1615
[
@@ -22,6 +21,6 @@ def setup_method(self):
2221
(["A", "B"], 0, 2),
2322
],
2423
)
25-
def test_least_interval(self, tasks: list[str], n: int, expected: int):
26-
result = run_least_interval(Solution, tasks, n)
24+
def test_least_interval(self, tasks: list[str], n: int, expected: int, solution_class: type):
25+
result = run_least_interval(solution_class, tasks, n)
2726
assert_least_interval(result, expected)

leetcode/validate_binary_search_tree/solution.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from collections import deque
2+
13
from leetcode_py import TreeNode
24

35

@@ -15,3 +17,45 @@ def validate(cls, node: TreeNode[int] | None, min_val: float, max_val: float) ->
1517
# Space: O(h)
1618
def is_valid_bst(self, root: TreeNode[int] | None) -> bool:
1719
return self.validate(root, float("-inf"), float("inf"))
20+
21+
22+
class SolutionDFS:
23+
# Time: O(n)
24+
# Space: O(h)
25+
def is_valid_bst(self, root: TreeNode[int] | None) -> bool:
26+
if not root:
27+
return True
28+
29+
stack = [(root, float("-inf"), float("inf"))]
30+
31+
while stack:
32+
node, min_val, max_val = stack.pop()
33+
if node.val <= min_val or node.val >= max_val:
34+
return False
35+
if node.right:
36+
stack.append((node.right, node.val, max_val))
37+
if node.left:
38+
stack.append((node.left, min_val, node.val))
39+
40+
return True
41+
42+
43+
class SolutionBFS:
44+
# Time: O(n)
45+
# Space: O(w) where w is max width
46+
def is_valid_bst(self, root: TreeNode[int] | None) -> bool:
47+
if not root:
48+
return True
49+
50+
queue = deque([(root, float("-inf"), float("inf"))])
51+
52+
while queue:
53+
node, min_val, max_val = queue.popleft()
54+
if node.val <= min_val or node.val >= max_val:
55+
return False
56+
if node.right:
57+
queue.append((node.right, node.val, max_val))
58+
if node.left:
59+
queue.append((node.left, min_val, node.val))
60+
61+
return True

leetcode/validate_binary_search_tree/test_solution.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
from leetcode_py.test_utils import logged_test
44

55
from .helpers import assert_is_valid_bst, run_is_valid_bst
6-
from .solution import Solution
6+
from .solution import Solution, SolutionBFS, SolutionDFS
77

88

99
class TestValidateBinarySearchTree:
10-
def setup_method(self):
11-
self.solution = Solution()
1210

1311
@logged_test
12+
@pytest.mark.parametrize("solution_class", [Solution, SolutionDFS, SolutionBFS])
1413
@pytest.mark.parametrize(
1514
"root_list, expected",
1615
[
@@ -22,6 +21,6 @@ def setup_method(self):
2221
([2, 1, 3, None, None, None, 4], True),
2322
],
2423
)
25-
def test_is_valid_bst(self, root_list: list[int | None], expected: bool):
26-
result = run_is_valid_bst(Solution, root_list)
24+
def test_is_valid_bst(self, root_list: list[int | None], expected: bool, solution_class: type):
25+
result = run_is_valid_bst(solution_class, root_list)
2726
assert_is_valid_bst(result, expected)

0 commit comments

Comments
 (0)