Skip to content

Commit 419625f

Browse files
authored
feat: add 12 more leetcode problems (#134)
2 parents 9041578 + b401594 commit 419625f

91 files changed

Lines changed: 2879 additions & 84 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/.dev/problem_lists/unscrapable.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
(261, "graph-valid-tree"),
1010
(271, "encode-and-decode-strings"),
1111
(323, "number-of-connected-components-in-an-undirected-graph"),
12+
(437, "path-sum-iii"), # Causing issues with next_problem.py - prefer manual creation
1213
# Add more unscrapable problems as discovered
1314
]
1415

.claude/commands/test-quality-assurance.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ rm -rf leetcode/{problem_name}_backup
3838
-**NEVER modify helpers.py manually** - let regeneration handle it
3939
-**NEVER skip ty verification** - this is the main CI issue
4040
-**NEVER assume tests will pass** - they may fail if solution is incomplete
41+
-**NEVER use `null` in JSON templates** - use `None` for Python None values
4142

4243
### 4. What to Do
4344

4445
-**ALWAYS use `bake p-gen -p {problem_name} -f`** for regeneration
4546
-**ALWAYS verify ty passes** before considering task complete
4647
-**ALWAYS restore original solution** after regeneration
4748
-**ALWAYS check JSON template** if ty fails (look for `assert_assert_` bugs)
49+
-**ALWAYS use `None` not `null` in JSON templates** for Python None values
4850

4951
## Test Case Standards
5052

@@ -57,6 +59,9 @@ rm -rf leetcode/{problem_name}_backup
5759

5860
### JSON Format
5961

62+
- **CRITICAL**: Use `None` NOT `null` for Python None values in test cases
63+
- JSON templates use `None` directly: `"[1, None, 2]"` NOT `"[1, null, 2]"`
64+
- This ensures generated Python code passes linting (ruff/ty check for undefined name `null`)
6065
- Use single quotes for Python strings: `'hello'` not `"hello"`
6166
- Follow existing parametrize format
6267
- Ensure valid Python list syntax in test_cases field
@@ -109,6 +114,20 @@ bake p-gen -p {problem_name} -f
109114
**Expected**: Tests may fail if solution is incomplete (returns 0 or placeholder)
110115
**Action**: This is normal - focus on ty passing, not test results
111116

117+
### Issue: `null` vs `None` in JSON Templates
118+
119+
**Cause**: JSON template uses `null` which causes linting errors in generated Python code
120+
121+
- Error: `F821 Undefined name 'null'` from ruff/ty
122+
- Generated test files contain `null` which is not valid Python
123+
124+
**Solution**: Update JSON template to use `None` instead of `null`
125+
126+
- Change: `"([1, null, 2], 3, 1)"``"([1, None, 2], 3, 1)"`
127+
- This applies to `test_cases` list and `playground_setup` fields
128+
- After fixing JSON, regenerate with `bake p-gen -p {problem_name} -f`
129+
- Generated code will now pass linting without manual edits
130+
112131
## Success Criteria
113132

114133
-**ty passes** with no errors (CRITICAL for CI)

.github/workflows/test-reproducibility.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
4444
- name: cache-graphviz
4545
id: cache-graphviz
46-
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
46+
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
4747
with:
4848
path: ~/graphviz-cache
4949
key: graphviz-installed-${{ runner.os }}

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ jobs:
5858
5959
- name: cache-graphviz
6060
id: cache-graphviz
61-
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
61+
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
6262
with:
6363
path: ~/graphviz-cache
6464
key: graphviz-installed-${{ runner.os }}

bakefile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from bake import Context, command, console
77
from bakelib import PythonLibSpace
88

9-
PROBLEM = "number_of_connected_components_in_an_undirected_graph"
9+
PROBLEM = "path_sum_iii"
1010
problem_option = Annotated[str, typer.Option("-p", "--problem")]
1111
force_option = Annotated[bool, typer.Option("-f", "--force")]
1212

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Cheapest Flights Within K Stops
2+
3+
**Difficulty:** Medium
4+
**Topics:** Dynamic Programming, Depth-First Search, Breadth-First Search, Graph Theory, Heap (Priority Queue), Shortest Path
5+
**Tags:** algo-master-75
6+
7+
**LeetCode:** [Problem 787](https://leetcode.com/problems/cheapest-flights-within-k-stops/description/)
8+
9+
## Problem Description
10+
11+
There are `n` cities connected by some number of flights. You are given an array `flights` where `flights[i] = [fromi, toi, pricei]` indicates that there is a flight from city `fromi` to city `toi` with cost `pricei`.
12+
13+
You are also given three integers `src`, `dst`, and `k`, return **the cheapest price** from `src` to `dst` with at most `k` stops. If there is no such route, return `-1`.
14+
15+
## Examples
16+
17+
### Example 1:
18+
19+
![Example 1](https://assets.leetcode.com/uploads/2022/03/18/cheapest-flights-within-k-stops-3drawio.png)
20+
21+
```
22+
Input: n = 4, flights = [[0,1,100],[1,2,100],[2,0,100],[1,3,600],[2,3,200]], src = 0, dst = 3, k = 1
23+
Output: 700
24+
Explanation:
25+
The graph is shown above.
26+
The optimal path with at most 1 stop from city 0 to 3 is marked in red and has cost 100 + 600 = 700.
27+
Note that the path through cities [0,1,2,3] is cheaper but is invalid because it uses 2 stops.
28+
```
29+
30+
### Example 2:
31+
32+
![Example 2](https://assets.leetcode.com/uploads/2022/03/18/cheapest-flights-within-k-stops-1drawio.png)
33+
34+
```
35+
Input: n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 1
36+
Output: 200
37+
Explanation:
38+
The graph is shown above.
39+
The optimal path with at most 1 stop from city 0 to 2 is marked in red and has cost 100 + 100 = 200.
40+
```
41+
42+
### Example 3:
43+
44+
![Example 3](https://assets.leetcode.com/uploads/2022/03/18/cheapest-flights-within-k-stops-2drawio.png)
45+
46+
```
47+
Input: n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 0
48+
Output: 500
49+
Explanation:
50+
The graph is shown above.
51+
The optimal path with no stops from city 0 to 2 is marked in red and has cost 500.
52+
```
53+
54+
## Constraints
55+
56+
- 2 <= n <= 100
57+
- 0 <= flights.length <= (n \* (n - 1) / 2)
58+
- flights[i].length == 3
59+
- 0 <= fromi, toi < n
60+
- fromi != toi
61+
- 1 <= pricei <= 10^4
62+
- There will not be any multiple flights between two cities.
63+
- 0 <= src, dst, k < n
64+
- src != dst

leetcode/cheapest_flights_within_k_stops/__init__.py

Whitespace-only changes.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
def run_find_cheapest_price(
2+
solution_class: type, n: int, flights: list[list[int]], src: int, dst: int, k: int
3+
):
4+
implementation = solution_class()
5+
return implementation.find_cheapest_price(n, flights, src, dst, k)
6+
7+
8+
def assert_find_cheapest_price(result: int, expected: int) -> bool:
9+
assert result == expected
10+
return True
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# ---
2+
# jupyter:
3+
# jupytext:
4+
# text_representation:
5+
# extension: .py
6+
# format_name: percent
7+
# format_version: '1.3'
8+
# jupytext_version: 1.19.1
9+
# kernelspec:
10+
# display_name: leetcode-py-py3.13
11+
# language: python
12+
# name: python3
13+
# ---
14+
15+
# %%
16+
from helpers import assert_find_cheapest_price, run_find_cheapest_price
17+
from solution import Solution
18+
19+
# %%
20+
# Example test case
21+
n = 4
22+
flights = [[0, 1, 100], [1, 2, 100], [2, 0, 100], [1, 3, 600], [2, 3, 200]]
23+
src = 0
24+
dst = 3
25+
k = 1
26+
expected = 700
27+
28+
# %%
29+
result = run_find_cheapest_price(Solution, n, flights, src, dst, k)
30+
result
31+
32+
# %%
33+
assert_find_cheapest_price(result, expected)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from collections import deque
2+
3+
4+
class Solution:
5+
# Time: O(K * E) where E is number of flights
6+
# Space: O(V) where V is number of cities
7+
def find_cheapest_price(
8+
self, n: int, flights: list[list[int]], src: int, dst: int, k: int
9+
) -> int:
10+
# Build adjacency list
11+
adj = [[] for _ in range(n)]
12+
for from_i, to_i, price_i in flights:
13+
adj[from_i].append((to_i, price_i))
14+
15+
# BFS with stops constraint
16+
prices = [float("inf")] * n
17+
prices[src] = 0
18+
queue = deque([(src, 0, 0)]) # (node, current_price, stops)
19+
20+
while queue:
21+
node, current_price, stops = queue.popleft()
22+
if stops > k:
23+
continue
24+
for neighbor, price in adj[node]:
25+
new_price = current_price + price
26+
if new_price < prices[neighbor]:
27+
prices[neighbor] = new_price
28+
queue.append((neighbor, new_price, stops + 1))
29+
30+
return int(prices[dst]) if prices[dst] != float("inf") else -1

0 commit comments

Comments
 (0)