From 4079793461735e6bd8b42d504fbac1d916d36ad4 Mon Sep 17 00:00:00 2001
From: Wisaroot <66859294+wisarootl@users.noreply.github.com>
Date: Sat, 13 Sep 2025 00:07:29 +0700
Subject: [PATCH 01/57] feat: add two_sum (#36)
---
.github/workflows/ci-test-reproducibility.yml | 46 ++++++++++
.templates/check_test_cases.py | 6 +-
.templates/leetcode/json/two_sum.json | 63 +++++++++++++
Makefile | 2 +-
leetcode/two_sum/README.md | 49 ++++++++++
leetcode/two_sum/__init__.py | 0
leetcode/two_sum/helpers.py | 8 ++
leetcode/two_sum/playground.ipynb | 92 +++++++++++++++++++
leetcode/two_sum/solution.py | 12 +++
leetcode/two_sum/test_solution.py | 39 ++++++++
10 files changed, 315 insertions(+), 2 deletions(-)
create mode 100644 .templates/leetcode/json/two_sum.json
create mode 100644 leetcode/two_sum/README.md
create mode 100644 leetcode/two_sum/__init__.py
create mode 100644 leetcode/two_sum/helpers.py
create mode 100644 leetcode/two_sum/playground.ipynb
create mode 100644 leetcode/two_sum/solution.py
create mode 100644 leetcode/two_sum/test_solution.py
diff --git a/.github/workflows/ci-test-reproducibility.yml b/.github/workflows/ci-test-reproducibility.yml
index 42c71c2..1be9cab 100644
--- a/.github/workflows/ci-test-reproducibility.yml
+++ b/.github/workflows/ci-test-reproducibility.yml
@@ -35,6 +35,40 @@ jobs:
- name: Install dependencies
run: poetry install --no-interaction --no-ansi
+ - name: Cache Graphviz installation
+ id: cache-graphviz
+ uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
+ with:
+ path: ~/graphviz-cache
+ key: graphviz-installed-${{ runner.os }}
+
+ - name: Install Graphviz
+ run: |
+ if [ "${{ steps.cache-graphviz.outputs.cache-hit }}" = "true" ]; then
+ sudo cp ~/graphviz-cache/bin/* /usr/bin/ 2>/dev/null || true
+ sudo cp ~/graphviz-cache/lib/* /usr/lib/x86_64-linux-gnu/ 2>/dev/null || true
+ sudo cp -r ~/graphviz-cache/share/graphviz /usr/share/ 2>/dev/null || true
+ sudo cp -r ~/graphviz-cache/lib/graphviz /usr/lib/x86_64-linux-gnu/ 2>/dev/null || true
+ sudo ldconfig
+ sudo dot -c
+ else
+ sudo apt-get update
+ sudo apt-get install -y graphviz
+ mkdir -p ~/graphviz-cache/{bin,lib,share}
+ cp /usr/bin/{dot,neato,twopi,circo,fdp,sfdp,patchwork,osage} ~/graphviz-cache/bin/ 2>/dev/null || true
+ cp /usr/lib/x86_64-linux-gnu/lib{gvc,cgraph,cdt,pathplan,gvpr,lab-gamut,ann,gts}* ~/graphviz-cache/lib/ 2>/dev/null || true
+ cp -r /usr/lib/x86_64-linux-gnu/graphviz ~/graphviz-cache/lib/ 2>/dev/null || true
+ cp -r /usr/share/graphviz ~/graphviz-cache/share/ 2>/dev/null || true
+ fi
+
+ - name: Check test case count
+ run: poetry run python .templates/check_test_cases.py --threshold=10 --max=100
+
+ - name: Backup existing problems
+ run: |
+ mkdir -p .cache
+ cp -r leetcode .cache/
+
- name: Delete existing problems
run: rm -rf leetcode/*/
@@ -46,3 +80,15 @@ jobs:
- name: Run linting to verify reproducibility
run: make lint
+
+ - name: Copy solution files from backup
+ run: |
+ for problem in .cache/leetcode/*/; do
+ problem_name=$(basename "$problem")
+ if [ -f "$problem/solution.py" ] && [ -d "leetcode/$problem_name" ]; then
+ cp "$problem/solution.py" "leetcode/$problem_name/solution.py"
+ fi
+ done
+
+ - name: Run tests
+ run: make test
diff --git a/.templates/check_test_cases.py b/.templates/check_test_cases.py
index 4c36ffe..a99a310 100644
--- a/.templates/check_test_cases.py
+++ b/.templates/check_test_cases.py
@@ -82,10 +82,14 @@ def main(
typer.echo(f"Invalid max_results value: {max_results}", err=True)
raise typer.Exit(1)
- typer.echo(f"Files with ≤{threshold} test cases ({len(filtered_files)} total):")
+ typer.echo(f"Problems with ≤{threshold} test cases ({len(filtered_files)} total):")
for filename, count in filtered_files:
typer.echo(f"{filename}: {count} test cases")
+ # Exit with non-zero code if any files found
+ if filtered_files:
+ raise typer.Exit(1)
+
if __name__ == "__main__":
typer.run(main)
diff --git a/.templates/leetcode/json/two_sum.json b/.templates/leetcode/json/two_sum.json
new file mode 100644
index 0000000..a1c69c3
--- /dev/null
+++ b/.templates/leetcode/json/two_sum.json
@@ -0,0 +1,63 @@
+{
+ "problem_name": "two_sum",
+ "solution_class_name": "Solution",
+ "problem_number": "1",
+ "problem_title": "Two Sum",
+ "difficulty": "Easy",
+ "topics": "Array, Hash Table",
+ "_tags": { "list": ["grind-75"] },
+ "readme_description": "Given an array of integers `nums` and an integer `target`, return indices of the two numbers such that they add up to `target`.\n\nYou may assume that each input would have exactly one solution, and you may not use the same element twice.\n\nYou can return the answer in any order.",
+ "_readme_examples": {
+ "list": [
+ {
+ "content": "```\nInput: nums = [2,7,11,15], target = 9\nOutput: [0,1]\n```\n**Explanation:** Because nums[0] + nums[1] == 9, we return [0, 1]."
+ },
+ { "content": "```\nInput: nums = [3,2,4], target = 6\nOutput: [1,2]\n```" },
+ { "content": "```\nInput: nums = [3,3], target = 6\nOutput: [0,1]\n```" }
+ ]
+ },
+ "readme_constraints": "- 2 <= nums.length <= 10^4\n- -10^9 <= nums[i] <= 10^9\n- -10^9 <= target <= 10^9\n- Only one valid answer exists.",
+ "readme_additional": "**Follow-up:** Can you come up with an algorithm that is less than O(n^2) time complexity?",
+ "helpers_imports": "",
+ "helpers_content": "",
+ "helpers_run_name": "two_sum",
+ "helpers_run_signature": "(solution_class: type, nums: list[int], target: int)",
+ "helpers_run_body": " implementation = solution_class()\n return implementation.two_sum(nums, target)",
+ "helpers_assert_name": "two_sum",
+ "helpers_assert_signature": "(result: list[int], expected: list[int]) -> bool",
+ "helpers_assert_body": " assert result == expected\n return True",
+ "solution_imports": "",
+ "solution_contents": "",
+ "solution_class_content": "",
+ "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_two_sum, run_two_sum\nfrom .solution import Solution",
+ "test_content": "",
+ "test_class_name": "TwoSum",
+ "test_class_content": " def setup_method(self):\n self.solution = Solution()",
+ "_solution_methods": {
+ "list": [
+ {
+ "name": "two_sum",
+ "signature": "(self, nums: list[int], target: int) -> list[int]",
+ "body": " # TODO: Implement two_sum\n return []"
+ }
+ ]
+ },
+ "_test_helper_methods": {
+ "list": [{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }]
+ },
+ "_test_methods": {
+ "list": [
+ {
+ "name": "test_two_sum",
+ "signature": "(self, nums: list[int], target: int, expected: list[int])",
+ "parametrize": "nums, target, expected",
+ "test_cases": "[([2, 7, 11, 15], 9, [0, 1]), ([3, 2, 4], 6, [1, 2]), ([3, 3], 6, [0, 1]), ([2, 5, 5, 11], 10, [1, 2]), ([1, 2, 3, 4, 5], 8, [2, 4]), ([0, 4, 3, 0], 0, [0, 3]), ([-1, -2, -3, -4, -5], -8, [2, 4]), ([1, 3, 4, 2], 6, [2, 3]), ([5, 75, 25], 100, [1, 2]), ([-3, 4, 3, 90], 0, [0, 2]), ([1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 2], 6, [5, 11]), ([2, 1, 9, 4, 4, 56, 90, 3], 8, [3, 4]), ([89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99], 185, [3, 4]), ([-1000000000, 1000000000], 0, [0, 1]), ([0, 1], 1, [0, 1]), ([1, 2], 5, []), ([3, 5, 7], 1, []), ([10, 20, 30], 15, [])]",
+ "body": " result = run_two_sum(Solution, nums, target)\n assert_two_sum(result, expected)"
+ }
+ ]
+ },
+ "playground_imports": "from helpers import run_two_sum, assert_two_sum\nfrom solution import Solution",
+ "playground_setup": "# Example test case\nnums = [2, 7, 11, 15]\ntarget = 9\nexpected = [0, 1]",
+ "playground_run": "result = run_two_sum(Solution, nums, target)\nresult",
+ "playground_assert": "assert_two_sum(result, expected)"
+}
diff --git a/Makefile b/Makefile
index 3bdc248..0f97dae 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
PYTHON_VERSION = 3.13
-PROBLEM ?= min_stack
+PROBLEM ?= two_sum
FORCE ?= 0
COMMA := ,
diff --git a/leetcode/two_sum/README.md b/leetcode/two_sum/README.md
new file mode 100644
index 0000000..7f8a41d
--- /dev/null
+++ b/leetcode/two_sum/README.md
@@ -0,0 +1,49 @@
+# Two Sum
+
+**Difficulty:** Easy
+**Topics:** Array, Hash Table
+**Tags:** grind-75
+
+**LeetCode:** [Problem 1](https://leetcode.com/problems/two-sum/description/)
+
+## Problem Description
+
+Given an array of integers `nums` and an integer `target`, return indices of the two numbers such that they add up to `target`.
+
+You may assume that each input would have exactly one solution, and you may not use the same element twice.
+
+You can return the answer in any order.
+
+## Examples
+
+### Example 1:
+
+```
+Input: nums = [2,7,11,15], target = 9
+Output: [0,1]
+```
+
+**Explanation:** Because nums[0] + nums[1] == 9, we return [0, 1].
+
+### Example 2:
+
+```
+Input: nums = [3,2,4], target = 6
+Output: [1,2]
+```
+
+### Example 3:
+
+```
+Input: nums = [3,3], target = 6
+Output: [0,1]
+```
+
+## Constraints
+
+- 2 <= nums.length <= 10^4
+- -10^9 <= nums[i] <= 10^9
+- -10^9 <= target <= 10^9
+- Only one valid answer exists.
+
+**Follow-up:** Can you come up with an algorithm that is less than O(n^2) time complexity?
diff --git a/leetcode/two_sum/__init__.py b/leetcode/two_sum/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/leetcode/two_sum/helpers.py b/leetcode/two_sum/helpers.py
new file mode 100644
index 0000000..00f7b73
--- /dev/null
+++ b/leetcode/two_sum/helpers.py
@@ -0,0 +1,8 @@
+def run_two_sum(solution_class: type, nums: list[int], target: int):
+ implementation = solution_class()
+ return implementation.two_sum(nums, target)
+
+
+def assert_two_sum(result: list[int], expected: list[int]) -> bool:
+ assert result == expected
+ return True
diff --git a/leetcode/two_sum/playground.ipynb b/leetcode/two_sum/playground.ipynb
new file mode 100644
index 0000000..1a6ec8b
--- /dev/null
+++ b/leetcode/two_sum/playground.ipynb
@@ -0,0 +1,92 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "imports",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from helpers import assert_two_sum, run_two_sum\n",
+ "from solution import Solution"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "setup",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Example test case\n",
+ "nums = [2, 7, 11, 15]\n",
+ "target = 9\n",
+ "expected = [0, 1]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "run",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[0, 1]"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "result = run_two_sum(Solution, nums, target)\n",
+ "result"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "assert",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "True"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "assert_two_sum(result, expected)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "leetcode-py-py3.13",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.13.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/leetcode/two_sum/solution.py b/leetcode/two_sum/solution.py
new file mode 100644
index 0000000..ea4c593
--- /dev/null
+++ b/leetcode/two_sum/solution.py
@@ -0,0 +1,12 @@
+class Solution:
+
+ # Time: O(n)
+ # Space: O(n)
+ def two_sum(self, nums: list[int], target: int) -> list[int]:
+ seen: dict[int, int] = {}
+ for i, num in enumerate(nums):
+ complement = target - num
+ if complement in seen:
+ return [seen[complement], i]
+ seen[num] = i
+ return []
diff --git a/leetcode/two_sum/test_solution.py b/leetcode/two_sum/test_solution.py
new file mode 100644
index 0000000..75cb3f3
--- /dev/null
+++ b/leetcode/two_sum/test_solution.py
@@ -0,0 +1,39 @@
+import pytest
+
+from leetcode_py.test_utils import logged_test
+
+from .helpers import assert_two_sum, run_two_sum
+from .solution import Solution
+
+
+class TestTwoSum:
+ def setup_method(self):
+ self.solution = Solution()
+
+ @logged_test
+ @pytest.mark.parametrize(
+ "nums, target, expected",
+ [
+ ([2, 7, 11, 15], 9, [0, 1]),
+ ([3, 2, 4], 6, [1, 2]),
+ ([3, 3], 6, [0, 1]),
+ ([2, 5, 5, 11], 10, [1, 2]),
+ ([1, 2, 3, 4, 5], 8, [2, 4]),
+ ([0, 4, 3, 0], 0, [0, 3]),
+ ([-1, -2, -3, -4, -5], -8, [2, 4]),
+ ([1, 3, 4, 2], 6, [2, 3]),
+ ([5, 75, 25], 100, [1, 2]),
+ ([-3, 4, 3, 90], 0, [0, 2]),
+ ([1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 2], 6, [5, 11]),
+ ([2, 1, 9, 4, 4, 56, 90, 3], 8, [3, 4]),
+ ([89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99], 185, [3, 4]),
+ ([-1000000000, 1000000000], 0, [0, 1]),
+ ([0, 1], 1, [0, 1]),
+ ([1, 2], 5, []),
+ ([3, 5, 7], 1, []),
+ ([10, 20, 30], 15, []),
+ ],
+ )
+ def test_two_sum(self, nums: list[int], target: int, expected: list[int]):
+ result = run_two_sum(Solution, nums, target)
+ assert_two_sum(result, expected)
From 9f2c846652153909a578b1c5730a985f2a892493 Mon Sep 17 00:00:00 2001
From: Wisaroot <66859294+wisarootl@users.noreply.github.com>
Date: Sat, 13 Sep 2025 01:09:34 +0700
Subject: [PATCH 02/57] feat: convert .ipynb to .py (#37)
---
.codecov.yml | 2 +
.github/workflows/ci-pre-commit.yml | 4 +
.pre-commit-config.yaml | 11 +
Makefile | 7 +
leetcode/accounts_merge/playground.ipynb | 102 ------
leetcode/accounts_merge/playground.py | 38 ++
leetcode/add_binary/playground.ipynb | 69 ----
leetcode/add_binary/playground.py | 30 ++
.../balanced_binary_tree/playground.ipynb | 183 ----------
leetcode/balanced_binary_tree/playground.py | 35 ++
leetcode/basic_calculator/playground.ipynb | 91 -----
leetcode/basic_calculator/playground.py | 30 ++
.../playground.ipynb | 91 -----
.../playground.py | 30 ++
leetcode/binary_search/playground.ipynb | 69 ----
leetcode/binary_search/playground.py | 31 ++
.../playground.ipynb | 70 ----
.../playground.py | 30 ++
.../playground.ipynb | 183 ----------
.../binary_tree_right_side_view/playground.py | 36 ++
.../test_solution.py | 9 +-
leetcode/climbing_stairs/playground.ipynb | 68 ----
leetcode/climbing_stairs/playground.py | 30 ++
leetcode/clone_graph/playground.ipynb | 69 ----
leetcode/clone_graph/playground.py | 29 ++
leetcode/coin_change/playground.ipynb | 69 ----
leetcode/coin_change/playground.py | 31 ++
leetcode/combination_sum/playground.ipynb | 69 ----
leetcode/combination_sum/playground.py | 31 ++
.../playground.ipynb | 68 ----
.../container_with_most_water/playground.py | 30 ++
leetcode/contains_duplicate/playground.ipynb | 91 -----
leetcode/contains_duplicate/playground.py | 30 ++
leetcode/course_schedule/playground.ipynb | 69 ----
leetcode/course_schedule/playground.py | 31 ++
.../diameter_of_binary_tree/playground.ipynb | 70 ----
.../diameter_of_binary_tree/playground.py | 30 ++
.../playground.ipynb | 68 ----
.../playground.py | 30 ++
.../playground.ipynb | 70 ----
.../playground.py | 32 ++
leetcode/first_bad_version/playground.ipynb | 69 ----
leetcode/first_bad_version/playground.py | 31 ++
leetcode/flood_fill/playground.ipynb | 71 ----
leetcode/flood_fill/playground.py | 33 ++
.../playground.ipynb | 70 ----
.../playground.py | 32 ++
.../playground.ipynb | 204 -----------
.../implement_trie_prefix_tree/playground.py | 32 ++
leetcode/insert_interval/playground.ipynb | 69 ----
leetcode/insert_interval/playground.py | 31 ++
leetcode/invert_binary_tree/playground.ipynb | 185 ----------
leetcode/invert_binary_tree/playground.py | 30 ++
leetcode/invert_binary_tree/test_solution.py | 9 +-
.../playground.ipynb | 69 ----
.../k_closest_points_to_origin/playground.py | 31 ++
.../playground.ipynb | 71 ----
.../playground.py | 31 ++
.../playground.ipynb | 68 ----
.../playground.py | 30 ++
leetcode/linked_list_cycle/playground.ipynb | 177 ----------
leetcode/linked_list_cycle/playground.py | 35 ++
leetcode/longest_palindrome/playground.ipynb | 68 ----
leetcode/longest_palindrome/playground.py | 30 ++
.../playground.ipynb | 68 ----
.../playground.py | 30 ++
.../test_solution.py | 7 +-
.../playground.ipynb | 68 ----
.../playground.py | 30 ++
.../playground.ipynb | 72 ----
.../playground.py | 32 ++
.../playground.ipynb | 72 ----
.../playground.py | 32 ++
leetcode/lru_cache/playground.ipynb | 100 ------
leetcode/lru_cache/playground.py | 32 ++
leetcode/lru_cache/test_solution.py | 13 +-
leetcode/majority_element/playground.ipynb | 68 ----
leetcode/majority_element/playground.py | 30 ++
.../playground.ipynb | 70 ----
.../playground.py | 30 ++
.../playground.ipynb | 70 ----
.../playground.py | 32 ++
leetcode/maximum_subarray/playground.ipynb | 68 ----
leetcode/maximum_subarray/playground.py | 30 ++
leetcode/merge_intervals/playground.ipynb | 68 ----
leetcode/merge_intervals/playground.py | 30 ++
.../merge_k_sorted_lists/playground.ipynb | 70 ----
leetcode/merge_k_sorted_lists/playground.py | 32 ++
.../merge_two_sorted_lists/playground.ipynb | 71 ----
leetcode/merge_two_sorted_lists/playground.py | 33 ++
.../playground.ipynb | 70 ----
.../middle_of_the_linked_list/playground.py | 32 ++
leetcode/min_stack/playground.ipynb | 69 ----
leetcode/min_stack/playground.py | 31 ++
.../minimum_height_trees/playground.ipynb | 69 ----
leetcode/minimum_height_trees/playground.py | 31 ++
.../minimum_window_substring/playground.ipynb | 69 ----
.../minimum_window_substring/playground.py | 31 ++
leetcode/number_of_islands/playground.ipynb | 73 ----
leetcode/number_of_islands/playground.py | 35 ++
.../playground.ipynb | 68 ----
.../partition_equal_subset_sum/playground.py | 30 ++
.../test_solution.py | 7 +-
leetcode/permutations/playground.ipynb | 68 ----
leetcode/permutations/playground.py | 30 ++
.../playground.ipynb | 68 ----
.../playground.py | 30 ++
leetcode/ransom_note/playground.ipynb | 69 ----
leetcode/ransom_note/playground.py | 31 ++
leetcode/reverse_linked_list/playground.ipynb | 70 ----
leetcode/reverse_linked_list/playground.py | 30 ++
.../reverse_linked_list_ii/playground.ipynb | 71 ----
leetcode/reverse_linked_list_ii/playground.py | 31 ++
leetcode/rotting_oranges/playground.ipynb | 68 ----
leetcode/rotting_oranges/playground.py | 30 ++
.../playground.ipynb | 69 ----
.../playground.py | 31 ++
.../playground.ipynb | 69 ----
.../playground.py | 29 ++
leetcode/sort_colors/playground.ipynb | 68 ----
leetcode/sort_colors/playground.py | 30 ++
leetcode/spiral_matrix/playground.ipynb | 68 ----
leetcode/spiral_matrix/playground.py | 30 ++
.../string_to_integer_atoi/playground.ipynb | 68 ----
leetcode/string_to_integer_atoi/playground.py | 30 ++
leetcode/task_scheduler/playground.ipynb | 69 ----
leetcode/task_scheduler/playground.py | 31 ++
leetcode/three_sum/playground.ipynb | 68 ----
leetcode/three_sum/playground.py | 30 ++
.../playground.ipynb | 69 ----
.../time_based_key_value_store/playground.py | 31 ++
leetcode/trapping_rain_water/playground.ipynb | 91 -----
leetcode/trapping_rain_water/playground.py | 30 ++
leetcode/two_sum/playground.ipynb | 92 -----
leetcode/two_sum/playground.py | 31 ++
leetcode/valid_anagram/playground.ipynb | 92 -----
leetcode/valid_anagram/playground.py | 31 ++
leetcode/valid_palindrome/playground.ipynb | 68 ----
leetcode/valid_palindrome/playground.py | 30 ++
leetcode/valid_parentheses/playground.ipynb | 68 ----
leetcode/valid_parentheses/playground.py | 30 ++
.../playground.ipynb | 70 ----
.../validate_binary_search_tree/playground.py | 30 ++
.../test_solution.py | 7 +-
leetcode/word_break/playground.ipynb | 69 ----
leetcode/word_break/playground.py | 31 ++
leetcode/word_ladder/playground.ipynb | 70 ----
leetcode/word_ladder/playground.py | 32 ++
leetcode/zero_one_matrix/playground.ipynb | 91 -----
leetcode/zero_one_matrix/playground.py | 30 ++
poetry.lock | 326 +++++++++++++++++-
pyproject.toml | 1 +
scripts/shared | 2 +-
sonar-project.properties | 2 +-
154 files changed, 2558 insertions(+), 5667 deletions(-)
delete mode 100644 leetcode/accounts_merge/playground.ipynb
create mode 100644 leetcode/accounts_merge/playground.py
delete mode 100644 leetcode/add_binary/playground.ipynb
create mode 100644 leetcode/add_binary/playground.py
delete mode 100644 leetcode/balanced_binary_tree/playground.ipynb
create mode 100644 leetcode/balanced_binary_tree/playground.py
delete mode 100644 leetcode/basic_calculator/playground.ipynb
create mode 100644 leetcode/basic_calculator/playground.py
delete mode 100644 leetcode/best_time_to_buy_and_sell_stock/playground.ipynb
create mode 100644 leetcode/best_time_to_buy_and_sell_stock/playground.py
delete mode 100644 leetcode/binary_search/playground.ipynb
create mode 100644 leetcode/binary_search/playground.py
delete mode 100644 leetcode/binary_tree_level_order_traversal/playground.ipynb
create mode 100644 leetcode/binary_tree_level_order_traversal/playground.py
delete mode 100644 leetcode/binary_tree_right_side_view/playground.ipynb
create mode 100644 leetcode/binary_tree_right_side_view/playground.py
delete mode 100644 leetcode/climbing_stairs/playground.ipynb
create mode 100644 leetcode/climbing_stairs/playground.py
delete mode 100644 leetcode/clone_graph/playground.ipynb
create mode 100644 leetcode/clone_graph/playground.py
delete mode 100644 leetcode/coin_change/playground.ipynb
create mode 100644 leetcode/coin_change/playground.py
delete mode 100644 leetcode/combination_sum/playground.ipynb
create mode 100644 leetcode/combination_sum/playground.py
delete mode 100644 leetcode/container_with_most_water/playground.ipynb
create mode 100644 leetcode/container_with_most_water/playground.py
delete mode 100644 leetcode/contains_duplicate/playground.ipynb
create mode 100644 leetcode/contains_duplicate/playground.py
delete mode 100644 leetcode/course_schedule/playground.ipynb
create mode 100644 leetcode/course_schedule/playground.py
delete mode 100644 leetcode/diameter_of_binary_tree/playground.ipynb
create mode 100644 leetcode/diameter_of_binary_tree/playground.py
delete mode 100644 leetcode/evaluate_reverse_polish_notation/playground.ipynb
create mode 100644 leetcode/evaluate_reverse_polish_notation/playground.py
delete mode 100644 leetcode/find_median_from_data_stream/playground.ipynb
create mode 100644 leetcode/find_median_from_data_stream/playground.py
delete mode 100644 leetcode/first_bad_version/playground.ipynb
create mode 100644 leetcode/first_bad_version/playground.py
delete mode 100644 leetcode/flood_fill/playground.ipynb
create mode 100644 leetcode/flood_fill/playground.py
delete mode 100644 leetcode/implement_queue_using_stacks/playground.ipynb
create mode 100644 leetcode/implement_queue_using_stacks/playground.py
delete mode 100644 leetcode/implement_trie_prefix_tree/playground.ipynb
create mode 100644 leetcode/implement_trie_prefix_tree/playground.py
delete mode 100644 leetcode/insert_interval/playground.ipynb
create mode 100644 leetcode/insert_interval/playground.py
delete mode 100644 leetcode/invert_binary_tree/playground.ipynb
create mode 100644 leetcode/invert_binary_tree/playground.py
delete mode 100644 leetcode/k_closest_points_to_origin/playground.ipynb
create mode 100644 leetcode/k_closest_points_to_origin/playground.py
delete mode 100644 leetcode/kth_smallest_element_in_a_bst/playground.ipynb
create mode 100644 leetcode/kth_smallest_element_in_a_bst/playground.py
delete mode 100644 leetcode/largest_rectangle_in_histogram/playground.ipynb
create mode 100644 leetcode/largest_rectangle_in_histogram/playground.py
delete mode 100644 leetcode/linked_list_cycle/playground.ipynb
create mode 100644 leetcode/linked_list_cycle/playground.py
delete mode 100644 leetcode/longest_palindrome/playground.ipynb
create mode 100644 leetcode/longest_palindrome/playground.py
delete mode 100644 leetcode/longest_palindromic_substring/playground.ipynb
create mode 100644 leetcode/longest_palindromic_substring/playground.py
delete mode 100644 leetcode/longest_substring_without_repeating_characters/playground.ipynb
create mode 100644 leetcode/longest_substring_without_repeating_characters/playground.py
delete mode 100644 leetcode/lowest_common_ancestor_of_a_binary_search_tree/playground.ipynb
create mode 100644 leetcode/lowest_common_ancestor_of_a_binary_search_tree/playground.py
delete mode 100644 leetcode/lowest_common_ancestor_of_a_binary_tree/playground.ipynb
create mode 100644 leetcode/lowest_common_ancestor_of_a_binary_tree/playground.py
delete mode 100644 leetcode/lru_cache/playground.ipynb
create mode 100644 leetcode/lru_cache/playground.py
delete mode 100644 leetcode/majority_element/playground.ipynb
create mode 100644 leetcode/majority_element/playground.py
delete mode 100644 leetcode/maximum_depth_of_binary_tree/playground.ipynb
create mode 100644 leetcode/maximum_depth_of_binary_tree/playground.py
delete mode 100644 leetcode/maximum_profit_in_job_scheduling/playground.ipynb
create mode 100644 leetcode/maximum_profit_in_job_scheduling/playground.py
delete mode 100644 leetcode/maximum_subarray/playground.ipynb
create mode 100644 leetcode/maximum_subarray/playground.py
delete mode 100644 leetcode/merge_intervals/playground.ipynb
create mode 100644 leetcode/merge_intervals/playground.py
delete mode 100644 leetcode/merge_k_sorted_lists/playground.ipynb
create mode 100644 leetcode/merge_k_sorted_lists/playground.py
delete mode 100644 leetcode/merge_two_sorted_lists/playground.ipynb
create mode 100644 leetcode/merge_two_sorted_lists/playground.py
delete mode 100644 leetcode/middle_of_the_linked_list/playground.ipynb
create mode 100644 leetcode/middle_of_the_linked_list/playground.py
delete mode 100644 leetcode/min_stack/playground.ipynb
create mode 100644 leetcode/min_stack/playground.py
delete mode 100644 leetcode/minimum_height_trees/playground.ipynb
create mode 100644 leetcode/minimum_height_trees/playground.py
delete mode 100644 leetcode/minimum_window_substring/playground.ipynb
create mode 100644 leetcode/minimum_window_substring/playground.py
delete mode 100644 leetcode/number_of_islands/playground.ipynb
create mode 100644 leetcode/number_of_islands/playground.py
delete mode 100644 leetcode/partition_equal_subset_sum/playground.ipynb
create mode 100644 leetcode/partition_equal_subset_sum/playground.py
delete mode 100644 leetcode/permutations/playground.ipynb
create mode 100644 leetcode/permutations/playground.py
delete mode 100644 leetcode/product_of_array_except_self/playground.ipynb
create mode 100644 leetcode/product_of_array_except_self/playground.py
delete mode 100644 leetcode/ransom_note/playground.ipynb
create mode 100644 leetcode/ransom_note/playground.py
delete mode 100644 leetcode/reverse_linked_list/playground.ipynb
create mode 100644 leetcode/reverse_linked_list/playground.py
delete mode 100644 leetcode/reverse_linked_list_ii/playground.ipynb
create mode 100644 leetcode/reverse_linked_list_ii/playground.py
delete mode 100644 leetcode/rotting_oranges/playground.ipynb
create mode 100644 leetcode/rotting_oranges/playground.py
delete mode 100644 leetcode/search_in_rotated_sorted_array/playground.ipynb
create mode 100644 leetcode/search_in_rotated_sorted_array/playground.py
delete mode 100644 leetcode/serialize_and_deserialize_binary_tree/playground.ipynb
create mode 100644 leetcode/serialize_and_deserialize_binary_tree/playground.py
delete mode 100644 leetcode/sort_colors/playground.ipynb
create mode 100644 leetcode/sort_colors/playground.py
delete mode 100644 leetcode/spiral_matrix/playground.ipynb
create mode 100644 leetcode/spiral_matrix/playground.py
delete mode 100644 leetcode/string_to_integer_atoi/playground.ipynb
create mode 100644 leetcode/string_to_integer_atoi/playground.py
delete mode 100644 leetcode/task_scheduler/playground.ipynb
create mode 100644 leetcode/task_scheduler/playground.py
delete mode 100644 leetcode/three_sum/playground.ipynb
create mode 100644 leetcode/three_sum/playground.py
delete mode 100644 leetcode/time_based_key_value_store/playground.ipynb
create mode 100644 leetcode/time_based_key_value_store/playground.py
delete mode 100644 leetcode/trapping_rain_water/playground.ipynb
create mode 100644 leetcode/trapping_rain_water/playground.py
delete mode 100644 leetcode/two_sum/playground.ipynb
create mode 100644 leetcode/two_sum/playground.py
delete mode 100644 leetcode/valid_anagram/playground.ipynb
create mode 100644 leetcode/valid_anagram/playground.py
delete mode 100644 leetcode/valid_palindrome/playground.ipynb
create mode 100644 leetcode/valid_palindrome/playground.py
delete mode 100644 leetcode/valid_parentheses/playground.ipynb
create mode 100644 leetcode/valid_parentheses/playground.py
delete mode 100644 leetcode/validate_binary_search_tree/playground.ipynb
create mode 100644 leetcode/validate_binary_search_tree/playground.py
delete mode 100644 leetcode/word_break/playground.ipynb
create mode 100644 leetcode/word_break/playground.py
delete mode 100644 leetcode/word_ladder/playground.ipynb
create mode 100644 leetcode/word_ladder/playground.py
delete mode 100644 leetcode/zero_one_matrix/playground.ipynb
create mode 100644 leetcode/zero_one_matrix/playground.py
diff --git a/.codecov.yml b/.codecov.yml
index 7c00c0a..c11c152 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -1,4 +1,6 @@
coverage:
+ ignore:
+ - "**/playground.py"
status:
project:
default:
diff --git a/.github/workflows/ci-pre-commit.yml b/.github/workflows/ci-pre-commit.yml
index 2e8ee70..2ff49f4 100644
--- a/.github/workflows/ci-pre-commit.yml
+++ b/.github/workflows/ci-pre-commit.yml
@@ -9,6 +9,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
+ with:
+ fetch-depth: 0
+ submodules: recursive
+ token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 5123e0d..23a8453 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -22,6 +22,17 @@ repos:
id: gitleaks
- repo: local
hooks:
+ - id: sync-submodules
+ name: Sync git submodules
+ entry: bash -c 'output=$(git submodule update --init --recursive --remote 2>&1); [ -z "$output" ]'
+ language: system
+ always_run: true
+ pass_filenames: false
+ - id: nb-to-py
+ name: convert notebooks to python
+ entry: make nb-to-py
+ language: system
+ pass_filenames: false
- id: lint
name: lint
entry: make lint
diff --git a/Makefile b/Makefile
index 0f97dae..377ff6d 100644
--- a/Makefile
+++ b/Makefile
@@ -77,6 +77,13 @@ p-gen:
p-del:
rm -rf leetcode/$(PROBLEM)
+# Convert all notebooks to .py files and delete .ipynb for better version control
+nb-to-py:
+ @echo "Converting all .ipynb files in leetcode/ to .py files..."
+ @find leetcode -name "*.ipynb" -exec poetry run jupytext --to py:percent {} \;
+ @find leetcode -name "*.ipynb" -delete
+ @echo "Conversion complete. All .ipynb files converted to .py and deleted."
+
# Generate All Problems - useful for people who fork this repo
gen-all-problems:
@echo "This will DELETE all existing problems and regenerate from JSON templates."
diff --git a/leetcode/accounts_merge/playground.ipynb b/leetcode/accounts_merge/playground.ipynb
deleted file mode 100644
index f0dcd43..0000000
--- a/leetcode/accounts_merge/playground.ipynb
+++ /dev/null
@@ -1,102 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_accounts_merge, run_accounts_merge\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "accounts = [\n",
- " [\"John\", \"johnsmith@mail.com\", \"john_newyork@mail.com\"],\n",
- " [\"John\", \"johnsmith@mail.com\", \"john00@mail.com\"],\n",
- " [\"Mary\", \"mary@mail.com\"],\n",
- " [\"John\", \"johnnybravo@mail.com\"],\n",
- "]\n",
- "expected = [\n",
- " [\"John\", \"john00@mail.com\", \"john_newyork@mail.com\", \"johnsmith@mail.com\"],\n",
- " [\"Mary\", \"mary@mail.com\"],\n",
- " [\"John\", \"johnnybravo@mail.com\"],\n",
- "]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "run",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "[['John', 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'],\n",
- " ['Mary', 'mary@mail.com'],\n",
- " ['John', 'johnnybravo@mail.com']]"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "result = run_accounts_merge(Solution, accounts)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "assert",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "assert_accounts_merge(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/accounts_merge/playground.py b/leetcode/accounts_merge/playground.py
new file mode 100644
index 0000000..d2ee36c
--- /dev/null
+++ b/leetcode/accounts_merge/playground.py
@@ -0,0 +1,38 @@
+# ---
+# jupyter:
+# jupytext:
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_accounts_merge, run_accounts_merge
+from solution import Solution
+
+# %%
+# Example test case
+accounts = [
+ ["John", "johnsmith@mail.com", "john_newyork@mail.com"],
+ ["John", "johnsmith@mail.com", "john00@mail.com"],
+ ["Mary", "mary@mail.com"],
+ ["John", "johnnybravo@mail.com"],
+]
+expected = [
+ ["John", "john00@mail.com", "john_newyork@mail.com", "johnsmith@mail.com"],
+ ["Mary", "mary@mail.com"],
+ ["John", "johnnybravo@mail.com"],
+]
+
+# %%
+result = run_accounts_merge(Solution, accounts)
+result
+
+# %%
+assert_accounts_merge(result, expected)
diff --git a/leetcode/add_binary/playground.ipynb b/leetcode/add_binary/playground.ipynb
deleted file mode 100644
index 0b2a93d..0000000
--- a/leetcode/add_binary/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_add_binary, run_add_binary\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "a = \"11\"\n",
- "b = \"1\"\n",
- "expected = \"100\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_add_binary(Solution, a, b)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_add_binary(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/add_binary/playground.py b/leetcode/add_binary/playground.py
new file mode 100644
index 0000000..41e3fdd
--- /dev/null
+++ b/leetcode/add_binary/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_add_binary, run_add_binary
+from solution import Solution
+
+# %%
+# Example test case
+a = "11"
+b = "1"
+expected = "100"
+
+# %%
+result = run_add_binary(Solution, a, b)
+result
+
+# %%
+assert_add_binary(result, expected)
diff --git a/leetcode/balanced_binary_tree/playground.ipynb b/leetcode/balanced_binary_tree/playground.ipynb
deleted file mode 100644
index e115cdb..0000000
--- a/leetcode/balanced_binary_tree/playground.ipynb
+++ /dev/null
@@ -1,183 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_is_balanced, run_is_balanced\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import TreeNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "root_list: list[int | None] = [3, 9, 20, None, None, 15, 7]\n",
- "expected = True"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "run",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "result = run_is_balanced(Solution, root_list)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "c01db53b",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "\n",
- "\n",
- "\n",
- "\n",
- "\n"
- ],
- "text/plain": [
- "TreeNode([3, 9, 20, None, None, 15, 7])"
- ]
- },
- "execution_count": 5,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "root = TreeNode.from_list(root_list)\n",
- "root"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "assert",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "assert_is_balanced(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/balanced_binary_tree/playground.py b/leetcode/balanced_binary_tree/playground.py
new file mode 100644
index 0000000..900c979
--- /dev/null
+++ b/leetcode/balanced_binary_tree/playground.py
@@ -0,0 +1,35 @@
+# ---
+# jupyter:
+# jupytext:
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_is_balanced, run_is_balanced
+from solution import Solution
+
+from leetcode_py import TreeNode
+
+# %%
+# Example test case
+root_list: list[int | None] = [3, 9, 20, None, None, 15, 7]
+expected = True
+
+# %%
+result = run_is_balanced(Solution, root_list)
+result
+
+# %%
+root = TreeNode.from_list(root_list)
+root
+
+# %%
+assert_is_balanced(result, expected)
diff --git a/leetcode/basic_calculator/playground.ipynb b/leetcode/basic_calculator/playground.ipynb
deleted file mode 100644
index d7681b7..0000000
--- a/leetcode/basic_calculator/playground.ipynb
+++ /dev/null
@@ -1,91 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_calculate, run_calculate\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "s = \"(1+(4+5+2)-3)+(6+8)\"\n",
- "expected = 23"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "run",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "23"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "result = run_calculate(Solution, s)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "assert",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "assert_calculate(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/basic_calculator/playground.py b/leetcode/basic_calculator/playground.py
new file mode 100644
index 0000000..261975f
--- /dev/null
+++ b/leetcode/basic_calculator/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_calculate, run_calculate
+from solution import Solution
+
+# %%
+# Example test case
+s = "(1+(4+5+2)-3)+(6+8)"
+expected = 23
+
+# %%
+result = run_calculate(Solution, s)
+result
+
+# %%
+assert_calculate(result, expected)
diff --git a/leetcode/best_time_to_buy_and_sell_stock/playground.ipynb b/leetcode/best_time_to_buy_and_sell_stock/playground.ipynb
deleted file mode 100644
index 81034bf..0000000
--- a/leetcode/best_time_to_buy_and_sell_stock/playground.ipynb
+++ /dev/null
@@ -1,91 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_max_profit, run_max_profit\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "prices = [7, 1, 5, 3, 6, 4]\n",
- "expected = 5"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "run",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "5"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "result = run_max_profit(Solution, prices)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "assert",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "assert_max_profit(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/best_time_to_buy_and_sell_stock/playground.py b/leetcode/best_time_to_buy_and_sell_stock/playground.py
new file mode 100644
index 0000000..e643984
--- /dev/null
+++ b/leetcode/best_time_to_buy_and_sell_stock/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_max_profit, run_max_profit
+from solution import Solution
+
+# %%
+# Example test case
+prices = [7, 1, 5, 3, 6, 4]
+expected = 5
+
+# %%
+result = run_max_profit(Solution, prices)
+result
+
+# %%
+assert_max_profit(result, expected)
diff --git a/leetcode/binary_search/playground.ipynb b/leetcode/binary_search/playground.ipynb
deleted file mode 100644
index 9c1a608..0000000
--- a/leetcode/binary_search/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_search, run_search\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "nums = [-1, 0, 3, 5, 9, 12]\n",
- "target = 9\n",
- "expected = 4"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_search(Solution, nums, target)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_search(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/binary_search/playground.py b/leetcode/binary_search/playground.py
new file mode 100644
index 0000000..02af2a1
--- /dev/null
+++ b/leetcode/binary_search/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_search, run_search
+from solution import Solution
+
+# %%
+# Example test case
+nums = [-1, 0, 3, 5, 9, 12]
+target = 9
+expected = 4
+
+# %%
+result = run_search(Solution, nums, target)
+result
+
+# %%
+assert_search(result, expected)
diff --git a/leetcode/binary_tree_level_order_traversal/playground.ipynb b/leetcode/binary_tree_level_order_traversal/playground.ipynb
deleted file mode 100644
index 9c98b39..0000000
--- a/leetcode/binary_tree_level_order_traversal/playground.ipynb
+++ /dev/null
@@ -1,70 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_level_order, run_level_order\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import TreeNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "root_list: list[int | None] = [3, 9, 20, None, None, 15, 7]\n",
- "expected = [[3], [9, 20], [15, 7]]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_level_order(Solution, root_list)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_level_order(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/binary_tree_level_order_traversal/playground.py b/leetcode/binary_tree_level_order_traversal/playground.py
new file mode 100644
index 0000000..42a84cd
--- /dev/null
+++ b/leetcode/binary_tree_level_order_traversal/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_level_order, run_level_order
+from solution import Solution
+
+# %%
+# Example test case
+root_list: list[int | None] = [3, 9, 20, None, None, 15, 7]
+expected = [[3], [9, 20], [15, 7]]
+
+# %%
+result = run_level_order(Solution, root_list)
+result
+
+# %%
+assert_level_order(result, expected)
diff --git a/leetcode/binary_tree_right_side_view/playground.ipynb b/leetcode/binary_tree_right_side_view/playground.ipynb
deleted file mode 100644
index 9187c4f..0000000
--- a/leetcode/binary_tree_right_side_view/playground.ipynb
+++ /dev/null
@@ -1,183 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_right_side_view, run_right_side_view\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import TreeNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "root_list: list[int | None] = [1, 2, 3, None, 5, None, 4]\n",
- "expected = [1, 3, 4]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "6ba11550",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "\n",
- "\n",
- "\n",
- "\n",
- "\n"
- ],
- "text/plain": [
- "TreeNode([1, 2, 3, None, 5, None, 4])"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "root = TreeNode.from_list(root_list)\n",
- "root"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "run",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "[1, 3, 4]"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "result = run_right_side_view(Solution, root_list)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "assert",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 5,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "assert_right_side_view(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/binary_tree_right_side_view/playground.py b/leetcode/binary_tree_right_side_view/playground.py
new file mode 100644
index 0000000..bbef9b5
--- /dev/null
+++ b/leetcode/binary_tree_right_side_view/playground.py
@@ -0,0 +1,36 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_right_side_view, run_right_side_view
+from solution import Solution
+
+from leetcode_py import TreeNode
+
+# %%
+# Example test case
+root_list: list[int | None] = [1, 2, 3, None, 5, None, 4]
+expected = [1, 3, 4]
+
+# %%
+root = TreeNode.from_list(root_list)
+root
+
+# %%
+result = run_right_side_view(Solution, root_list)
+result
+
+# %%
+assert_right_side_view(result, expected)
diff --git a/leetcode/binary_tree_right_side_view/test_solution.py b/leetcode/binary_tree_right_side_view/test_solution.py
index 2a1ca50..871ecef 100644
--- a/leetcode/binary_tree_right_side_view/test_solution.py
+++ b/leetcode/binary_tree_right_side_view/test_solution.py
@@ -3,7 +3,7 @@
from leetcode_py.test_utils import logged_test
from .helpers import assert_right_side_view, run_right_side_view
-from .solution import Solution
+from .solution import Solution, SolutionBFS, SolutionDFS
class TestBinaryTreeRightSideView:
@@ -11,6 +11,7 @@ def setup_method(self):
self.solution = Solution()
@logged_test
+ @pytest.mark.parametrize("solution_class", [Solution, SolutionDFS, SolutionBFS])
@pytest.mark.parametrize(
"root_list, expected",
[
@@ -30,6 +31,8 @@ def setup_method(self):
([10, 5, 15, None, 6, 12, 20], [10, 15, 20]),
],
)
- def test_right_side_view(self, root_list: list[int | None], expected: list[int]):
- result = run_right_side_view(Solution, root_list)
+ def test_right_side_view(
+ self, solution_class: type, root_list: list[int | None], expected: list[int]
+ ):
+ result = run_right_side_view(solution_class, root_list)
assert_right_side_view(result, expected)
diff --git a/leetcode/climbing_stairs/playground.ipynb b/leetcode/climbing_stairs/playground.ipynb
deleted file mode 100644
index 6fd5d25..0000000
--- a/leetcode/climbing_stairs/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_climb_stairs, run_climb_stairs\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "n = 3\n",
- "expected = 3"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_climb_stairs(Solution, n)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_climb_stairs(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/climbing_stairs/playground.py b/leetcode/climbing_stairs/playground.py
new file mode 100644
index 0000000..51601a5
--- /dev/null
+++ b/leetcode/climbing_stairs/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_climb_stairs, run_climb_stairs
+from solution import Solution
+
+# %%
+# Example test case
+n = 3
+expected = 3
+
+# %%
+result = run_climb_stairs(Solution, n)
+result
+
+# %%
+assert_climb_stairs(result, expected)
diff --git a/leetcode/clone_graph/playground.ipynb b/leetcode/clone_graph/playground.ipynb
deleted file mode 100644
index bb2a422..0000000
--- a/leetcode/clone_graph/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_clone_graph, run_clone_graph\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import GraphNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "adj_list = [[2, 4], [1, 3], [2, 4], [1, 3]]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_clone_graph(Solution, adj_list)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_clone_graph(result, adj_list)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/clone_graph/playground.py b/leetcode/clone_graph/playground.py
new file mode 100644
index 0000000..509f051
--- /dev/null
+++ b/leetcode/clone_graph/playground.py
@@ -0,0 +1,29 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_clone_graph, run_clone_graph
+from solution import Solution
+
+# %%
+# Example test case
+adj_list = [[2, 4], [1, 3], [2, 4], [1, 3]]
+
+# %%
+result = run_clone_graph(Solution, adj_list)
+result
+
+# %%
+assert_clone_graph(result, adj_list)
diff --git a/leetcode/coin_change/playground.ipynb b/leetcode/coin_change/playground.ipynb
deleted file mode 100644
index 580d39c..0000000
--- a/leetcode/coin_change/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_coin_change, run_coin_change\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "coins = [1, 2, 5]\n",
- "amount = 11\n",
- "expected = 3"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_coin_change(Solution, coins, amount)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_coin_change(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/coin_change/playground.py b/leetcode/coin_change/playground.py
new file mode 100644
index 0000000..99c925d
--- /dev/null
+++ b/leetcode/coin_change/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_coin_change, run_coin_change
+from solution import Solution
+
+# %%
+# Example test case
+coins = [1, 2, 5]
+amount = 11
+expected = 3
+
+# %%
+result = run_coin_change(Solution, coins, amount)
+result
+
+# %%
+assert_coin_change(result, expected)
diff --git a/leetcode/combination_sum/playground.ipynb b/leetcode/combination_sum/playground.ipynb
deleted file mode 100644
index ac0711f..0000000
--- a/leetcode/combination_sum/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_combination_sum, run_combination_sum\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "candidates = [2, 3, 6, 7]\n",
- "target = 7\n",
- "expected = [[2, 2, 3], [7]]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_combination_sum(Solution, candidates, target)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_combination_sum(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/combination_sum/playground.py b/leetcode/combination_sum/playground.py
new file mode 100644
index 0000000..c971550
--- /dev/null
+++ b/leetcode/combination_sum/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_combination_sum, run_combination_sum
+from solution import Solution
+
+# %%
+# Example test case
+candidates = [2, 3, 6, 7]
+target = 7
+expected = [[2, 2, 3], [7]]
+
+# %%
+result = run_combination_sum(Solution, candidates, target)
+result
+
+# %%
+assert_combination_sum(result, expected)
diff --git a/leetcode/container_with_most_water/playground.ipynb b/leetcode/container_with_most_water/playground.ipynb
deleted file mode 100644
index a95df0a..0000000
--- a/leetcode/container_with_most_water/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_max_area, run_max_area\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "height = [1, 8, 6, 2, 5, 4, 8, 3, 7]\n",
- "expected = 49"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_max_area(Solution, height)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_max_area(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/container_with_most_water/playground.py b/leetcode/container_with_most_water/playground.py
new file mode 100644
index 0000000..6d9f266
--- /dev/null
+++ b/leetcode/container_with_most_water/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_max_area, run_max_area
+from solution import Solution
+
+# %%
+# Example test case
+height = [1, 8, 6, 2, 5, 4, 8, 3, 7]
+expected = 49
+
+# %%
+result = run_max_area(Solution, height)
+result
+
+# %%
+assert_max_area(result, expected)
diff --git a/leetcode/contains_duplicate/playground.ipynb b/leetcode/contains_duplicate/playground.ipynb
deleted file mode 100644
index f43d0de..0000000
--- a/leetcode/contains_duplicate/playground.ipynb
+++ /dev/null
@@ -1,91 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_contains_duplicate, run_contains_duplicate\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "nums = [1, 2, 3, 1]\n",
- "expected = True"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "run",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "result = run_contains_duplicate(Solution, nums)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "assert",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "assert_contains_duplicate(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/contains_duplicate/playground.py b/leetcode/contains_duplicate/playground.py
new file mode 100644
index 0000000..84b6064
--- /dev/null
+++ b/leetcode/contains_duplicate/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_contains_duplicate, run_contains_duplicate
+from solution import Solution
+
+# %%
+# Example test case
+nums = [1, 2, 3, 1]
+expected = True
+
+# %%
+result = run_contains_duplicate(Solution, nums)
+result
+
+# %%
+assert_contains_duplicate(result, expected)
diff --git a/leetcode/course_schedule/playground.ipynb b/leetcode/course_schedule/playground.ipynb
deleted file mode 100644
index 2dfeb00..0000000
--- a/leetcode/course_schedule/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_can_finish, run_can_finish\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "num_courses = 2\n",
- "prerequisites = [[1, 0]]\n",
- "expected = True"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_can_finish(Solution, num_courses, prerequisites)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_can_finish(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/course_schedule/playground.py b/leetcode/course_schedule/playground.py
new file mode 100644
index 0000000..f1e4aef
--- /dev/null
+++ b/leetcode/course_schedule/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_can_finish, run_can_finish
+from solution import Solution
+
+# %%
+# Example test case
+num_courses = 2
+prerequisites = [[1, 0]]
+expected = True
+
+# %%
+result = run_can_finish(Solution, num_courses, prerequisites)
+result
+
+# %%
+assert_can_finish(result, expected)
diff --git a/leetcode/diameter_of_binary_tree/playground.ipynb b/leetcode/diameter_of_binary_tree/playground.ipynb
deleted file mode 100644
index d401bcf..0000000
--- a/leetcode/diameter_of_binary_tree/playground.ipynb
+++ /dev/null
@@ -1,70 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_diameter_of_binary_tree, run_diameter_of_binary_tree\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import TreeNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "root_list: list[int | None] = [1, 2, 3, 4, 5]\n",
- "expected = 3"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_diameter_of_binary_tree(Solution, root_list)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_diameter_of_binary_tree(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/diameter_of_binary_tree/playground.py b/leetcode/diameter_of_binary_tree/playground.py
new file mode 100644
index 0000000..afd819a
--- /dev/null
+++ b/leetcode/diameter_of_binary_tree/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_diameter_of_binary_tree, run_diameter_of_binary_tree
+from solution import Solution
+
+# %%
+# Example test case
+root_list: list[int | None] = [1, 2, 3, 4, 5]
+expected = 3
+
+# %%
+result = run_diameter_of_binary_tree(Solution, root_list)
+result
+
+# %%
+assert_diameter_of_binary_tree(result, expected)
diff --git a/leetcode/evaluate_reverse_polish_notation/playground.ipynb b/leetcode/evaluate_reverse_polish_notation/playground.ipynb
deleted file mode 100644
index 01fd8b8..0000000
--- a/leetcode/evaluate_reverse_polish_notation/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_eval_rpn, run_eval_rpn\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "tokens = [\"2\", \"1\", \"+\", \"3\", \"*\"]\n",
- "expected = 9"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_eval_rpn(Solution, tokens)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_eval_rpn(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/evaluate_reverse_polish_notation/playground.py b/leetcode/evaluate_reverse_polish_notation/playground.py
new file mode 100644
index 0000000..8ecfa9f
--- /dev/null
+++ b/leetcode/evaluate_reverse_polish_notation/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_eval_rpn, run_eval_rpn
+from solution import Solution
+
+# %%
+# Example test case
+tokens = ["2", "1", "+", "3", "*"]
+expected = 9
+
+# %%
+result = run_eval_rpn(Solution, tokens)
+result
+
+# %%
+assert_eval_rpn(result, expected)
diff --git a/leetcode/find_median_from_data_stream/playground.ipynb b/leetcode/find_median_from_data_stream/playground.ipynb
deleted file mode 100644
index 3a72e85..0000000
--- a/leetcode/find_median_from_data_stream/playground.ipynb
+++ /dev/null
@@ -1,70 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_median_finder, run_median_finder\n",
- "from solution import MedianFinder"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "operations = [\"MedianFinder\", \"addNum\", \"addNum\", \"findMedian\", \"addNum\", \"findMedian\"]\n",
- "inputs = [[], [1], [2], [], [3], []]\n",
- "expected = [None, None, None, 1.5, None, 2.0]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result, mf = run_median_finder(MedianFinder, operations, inputs)\n",
- "print(result)\n",
- "mf"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_median_finder(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/find_median_from_data_stream/playground.py b/leetcode/find_median_from_data_stream/playground.py
new file mode 100644
index 0000000..351a75a
--- /dev/null
+++ b/leetcode/find_median_from_data_stream/playground.py
@@ -0,0 +1,32 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_median_finder, run_median_finder
+from solution import MedianFinder
+
+# %%
+# Example test case
+operations = ["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
+inputs = [[], [1], [2], [], [3], []]
+expected = [None, None, None, 1.5, None, 2.0]
+
+# %%
+result, mf = run_median_finder(MedianFinder, operations, inputs)
+print(result)
+mf
+
+# %%
+assert_median_finder(result, expected)
diff --git a/leetcode/first_bad_version/playground.ipynb b/leetcode/first_bad_version/playground.ipynb
deleted file mode 100644
index eff1efe..0000000
--- a/leetcode/first_bad_version/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_first_bad_version, run_first_bad_version\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "n = 5\n",
- "bad = 4\n",
- "expected = 4"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_first_bad_version(Solution, n, bad)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_first_bad_version(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/first_bad_version/playground.py b/leetcode/first_bad_version/playground.py
new file mode 100644
index 0000000..34af8a1
--- /dev/null
+++ b/leetcode/first_bad_version/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_first_bad_version, run_first_bad_version
+from solution import Solution
+
+# %%
+# Example test case
+n = 5
+bad = 4
+expected = 4
+
+# %%
+result = run_first_bad_version(Solution, n, bad)
+result
+
+# %%
+assert_first_bad_version(result, expected)
diff --git a/leetcode/flood_fill/playground.ipynb b/leetcode/flood_fill/playground.ipynb
deleted file mode 100644
index 583f7a1..0000000
--- a/leetcode/flood_fill/playground.ipynb
+++ /dev/null
@@ -1,71 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_flood_fill, run_flood_fill\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "image = [[1, 1, 1], [1, 1, 0], [1, 0, 1]]\n",
- "sr = 1\n",
- "sc = 1\n",
- "color = 2\n",
- "expected = [[2, 2, 2], [2, 2, 0], [2, 0, 1]]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_flood_fill(Solution, image, sr, sc, color)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_flood_fill(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/flood_fill/playground.py b/leetcode/flood_fill/playground.py
new file mode 100644
index 0000000..674fb9c
--- /dev/null
+++ b/leetcode/flood_fill/playground.py
@@ -0,0 +1,33 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_flood_fill, run_flood_fill
+from solution import Solution
+
+# %%
+# Example test case
+image = [[1, 1, 1], [1, 1, 0], [1, 0, 1]]
+sr = 1
+sc = 1
+color = 2
+expected = [[2, 2, 2], [2, 2, 0], [2, 0, 1]]
+
+# %%
+result = run_flood_fill(Solution, image, sr, sc, color)
+result
+
+# %%
+assert_flood_fill(result, expected)
diff --git a/leetcode/implement_queue_using_stacks/playground.ipynb b/leetcode/implement_queue_using_stacks/playground.ipynb
deleted file mode 100644
index 735a590..0000000
--- a/leetcode/implement_queue_using_stacks/playground.ipynb
+++ /dev/null
@@ -1,70 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_my_queue, run_my_queue\n",
- "from solution import MyQueue"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "operations = [\"MyQueue\", \"push\", \"push\", \"peek\", \"pop\", \"empty\"]\n",
- "inputs = [[], [1], [2], [], [], []]\n",
- "expected = [None, None, None, 1, 1, False]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result, queue = run_my_queue(MyQueue, operations, inputs)\n",
- "print(result)\n",
- "queue"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_my_queue(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/implement_queue_using_stacks/playground.py b/leetcode/implement_queue_using_stacks/playground.py
new file mode 100644
index 0000000..834b6b8
--- /dev/null
+++ b/leetcode/implement_queue_using_stacks/playground.py
@@ -0,0 +1,32 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_my_queue, run_my_queue
+from solution import MyQueue
+
+# %%
+# Example test case
+operations = ["MyQueue", "push", "push", "peek", "pop", "empty"]
+inputs = [[], [1], [2], [], [], []]
+expected = [None, None, None, 1, 1, False]
+
+# %%
+result, queue = run_my_queue(MyQueue, operations, inputs)
+print(result)
+queue
+
+# %%
+assert_my_queue(result, expected)
diff --git a/leetcode/implement_trie_prefix_tree/playground.ipynb b/leetcode/implement_trie_prefix_tree/playground.ipynb
deleted file mode 100644
index c509af4..0000000
--- a/leetcode/implement_trie_prefix_tree/playground.ipynb
+++ /dev/null
@@ -1,204 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_trie_operations, run_trie_operations\n",
- "from solution import Trie"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "operations = [\"Trie\", \"insert\", \"search\", \"search\", \"starts_with\", \"insert\", \"search\"]\n",
- "inputs = [[], [\"apple\"], [\"apple\"], [\"app\"], [\"app\"], [\"app\"], [\"app\"]]\n",
- "expected = [None, None, True, False, True, None, True]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "run",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "[None, None, True, False, True, None, True]\n"
- ]
- },
- {
- "data": {
- "text/html": [
- "\n",
- "\n",
- "\n",
- "\n",
- "\n"
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "result, trie = run_trie_operations(Trie, operations, inputs)\n",
- "print(result)\n",
- "trie"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "assert",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "assert_trie_operations(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/implement_trie_prefix_tree/playground.py b/leetcode/implement_trie_prefix_tree/playground.py
new file mode 100644
index 0000000..815d6f6
--- /dev/null
+++ b/leetcode/implement_trie_prefix_tree/playground.py
@@ -0,0 +1,32 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_trie_operations, run_trie_operations
+from solution import Trie
+
+# %%
+# Example test case
+operations = ["Trie", "insert", "search", "search", "starts_with", "insert", "search"]
+inputs = [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
+expected = [None, None, True, False, True, None, True]
+
+# %%
+result, trie = run_trie_operations(Trie, operations, inputs)
+print(result)
+trie
+
+# %%
+assert_trie_operations(result, expected)
diff --git a/leetcode/insert_interval/playground.ipynb b/leetcode/insert_interval/playground.ipynb
deleted file mode 100644
index 0e8d78f..0000000
--- a/leetcode/insert_interval/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_insert, run_insert\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "intervals = [[1, 3], [6, 9]]\n",
- "new_interval = [2, 5]\n",
- "expected = [[1, 5], [6, 9]]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_insert(Solution, intervals, new_interval)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_insert(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/insert_interval/playground.py b/leetcode/insert_interval/playground.py
new file mode 100644
index 0000000..d895d17
--- /dev/null
+++ b/leetcode/insert_interval/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_insert, run_insert
+from solution import Solution
+
+# %%
+# Example test case
+intervals = [[1, 3], [6, 9]]
+new_interval = [2, 5]
+expected = [[1, 5], [6, 9]]
+
+# %%
+result = run_insert(Solution, intervals, new_interval)
+result
+
+# %%
+assert_insert(result, expected)
diff --git a/leetcode/invert_binary_tree/playground.ipynb b/leetcode/invert_binary_tree/playground.ipynb
deleted file mode 100644
index ba35a6c..0000000
--- a/leetcode/invert_binary_tree/playground.ipynb
+++ /dev/null
@@ -1,185 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_invert_tree, run_invert_tree\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import TreeNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "root_list: list[int | None] = [4, 2, 7, 1, 3, 6, 9]\n",
- "expected_list: list[int | None] = [4, 7, 2, 9, 6, 3, 1]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "run",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "\n",
- "\n",
- "\n",
- "\n",
- "\n"
- ],
- "text/plain": [
- "TreeNode([4, 7, 2, 9, 6, 3, 1])"
- ]
- },
- "execution_count": 7,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "result = run_invert_tree(Solution, root_list)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "assert",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "assert_invert_tree(result, expected_list)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/invert_binary_tree/playground.py b/leetcode/invert_binary_tree/playground.py
new file mode 100644
index 0000000..2805ae6
--- /dev/null
+++ b/leetcode/invert_binary_tree/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_invert_tree, run_invert_tree
+from solution import Solution
+
+# %%
+# Example test case
+root_list: list[int | None] = [4, 2, 7, 1, 3, 6, 9]
+expected_list: list[int | None] = [4, 7, 2, 9, 6, 3, 1]
+
+# %%
+result = run_invert_tree(Solution, root_list)
+result
+
+# %%
+assert_invert_tree(result, expected_list)
diff --git a/leetcode/invert_binary_tree/test_solution.py b/leetcode/invert_binary_tree/test_solution.py
index f20e784..9e40afb 100644
--- a/leetcode/invert_binary_tree/test_solution.py
+++ b/leetcode/invert_binary_tree/test_solution.py
@@ -3,7 +3,7 @@
from leetcode_py.test_utils import logged_test
from .helpers import assert_invert_tree, run_invert_tree
-from .solution import Solution
+from .solution import Solution, SolutionBFS, SolutionDFS
class TestInvertBinaryTree:
@@ -11,6 +11,7 @@ def setup_method(self):
self.solution = Solution()
@logged_test
+ @pytest.mark.parametrize("solution_class", [Solution, SolutionDFS, SolutionBFS])
@pytest.mark.parametrize(
"root_list, expected_list",
[
@@ -31,6 +32,8 @@ def setup_method(self):
([1, 2, 3, None, 4, None, 5], [1, 3, 2, 5, None, 4]),
],
)
- def test_invert_tree(self, root_list: list[int | None], expected_list: list[int | None]):
- result = run_invert_tree(Solution, root_list)
+ def test_invert_tree(
+ self, solution_class: type, root_list: list[int | None], expected_list: list[int | None]
+ ):
+ result = run_invert_tree(solution_class, root_list)
assert_invert_tree(result, expected_list)
diff --git a/leetcode/k_closest_points_to_origin/playground.ipynb b/leetcode/k_closest_points_to_origin/playground.ipynb
deleted file mode 100644
index fce66ec..0000000
--- a/leetcode/k_closest_points_to_origin/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_k_closest, run_k_closest\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "points = [[1, 3], [-2, 2]]\n",
- "k = 1\n",
- "expected = [[-2, 2]]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_k_closest(Solution, points, k)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_k_closest(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/k_closest_points_to_origin/playground.py b/leetcode/k_closest_points_to_origin/playground.py
new file mode 100644
index 0000000..5efc6f0
--- /dev/null
+++ b/leetcode/k_closest_points_to_origin/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_k_closest, run_k_closest
+from solution import Solution
+
+# %%
+# Example test case
+points = [[1, 3], [-2, 2]]
+k = 1
+expected = [[-2, 2]]
+
+# %%
+result = run_k_closest(Solution, points, k)
+result
+
+# %%
+assert_k_closest(result, expected)
diff --git a/leetcode/kth_smallest_element_in_a_bst/playground.ipynb b/leetcode/kth_smallest_element_in_a_bst/playground.ipynb
deleted file mode 100644
index b65acaf..0000000
--- a/leetcode/kth_smallest_element_in_a_bst/playground.ipynb
+++ /dev/null
@@ -1,71 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_kth_smallest, run_kth_smallest\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import TreeNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "root_list = [3, 1, 4, None, 2]\n",
- "k = 1\n",
- "expected = 1"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_kth_smallest(Solution, root_list, k)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_kth_smallest(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/kth_smallest_element_in_a_bst/playground.py b/leetcode/kth_smallest_element_in_a_bst/playground.py
new file mode 100644
index 0000000..6cff3f7
--- /dev/null
+++ b/leetcode/kth_smallest_element_in_a_bst/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_kth_smallest, run_kth_smallest
+from solution import Solution
+
+# %%
+# Example test case
+root_list = [3, 1, 4, None, 2]
+k = 1
+expected = 1
+
+# %%
+result = run_kth_smallest(Solution, root_list, k)
+result
+
+# %%
+assert_kth_smallest(result, expected)
diff --git a/leetcode/largest_rectangle_in_histogram/playground.ipynb b/leetcode/largest_rectangle_in_histogram/playground.ipynb
deleted file mode 100644
index 8470983..0000000
--- a/leetcode/largest_rectangle_in_histogram/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_largest_rectangle_area, run_largest_rectangle_area\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "heights = [2, 1, 5, 6, 2, 3]\n",
- "expected = 10"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_largest_rectangle_area(Solution, heights)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_largest_rectangle_area(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/largest_rectangle_in_histogram/playground.py b/leetcode/largest_rectangle_in_histogram/playground.py
new file mode 100644
index 0000000..16761db
--- /dev/null
+++ b/leetcode/largest_rectangle_in_histogram/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_largest_rectangle_area, run_largest_rectangle_area
+from solution import Solution
+
+# %%
+# Example test case
+heights = [2, 1, 5, 6, 2, 3]
+expected = 10
+
+# %%
+result = run_largest_rectangle_area(Solution, heights)
+result
+
+# %%
+assert_largest_rectangle_area(result, expected)
diff --git a/leetcode/linked_list_cycle/playground.ipynb b/leetcode/linked_list_cycle/playground.ipynb
deleted file mode 100644
index f4adc95..0000000
--- a/leetcode/linked_list_cycle/playground.ipynb
+++ /dev/null
@@ -1,177 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_has_cycle, create_cycle_list, run_has_cycle\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "values = [3, 2, 0, -4]\n",
- "pos = 1\n",
- "expected = True"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "66f97343",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "\n",
- "\n",
- "\n",
- "\n",
- "\n"
- ],
- "text/plain": [
- "ListNode([3, 2, 0, -4])"
- ]
- },
- "execution_count": 5,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "head = create_cycle_list(values, pos)\n",
- "head"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "run",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "result = run_has_cycle(Solution, values, pos)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "assert",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "assert_has_cycle(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/linked_list_cycle/playground.py b/leetcode/linked_list_cycle/playground.py
new file mode 100644
index 0000000..b19e7f6
--- /dev/null
+++ b/leetcode/linked_list_cycle/playground.py
@@ -0,0 +1,35 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_has_cycle, create_cycle_list, run_has_cycle
+from solution import Solution
+
+# %%
+# Example test case
+values = [3, 2, 0, -4]
+pos = 1
+expected = True
+
+# %%
+head = create_cycle_list(values, pos)
+head
+
+# %%
+result = run_has_cycle(Solution, values, pos)
+result
+
+# %%
+assert_has_cycle(result, expected)
diff --git a/leetcode/longest_palindrome/playground.ipynb b/leetcode/longest_palindrome/playground.ipynb
deleted file mode 100644
index 31ff59e..0000000
--- a/leetcode/longest_palindrome/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_longest_palindrome, run_longest_palindrome\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "s = \"abccccdd\"\n",
- "expected = 7"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_longest_palindrome(Solution, s)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_longest_palindrome(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/longest_palindrome/playground.py b/leetcode/longest_palindrome/playground.py
new file mode 100644
index 0000000..d84ed13
--- /dev/null
+++ b/leetcode/longest_palindrome/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_longest_palindrome, run_longest_palindrome
+from solution import Solution
+
+# %%
+# Example test case
+s = "abccccdd"
+expected = 7
+
+# %%
+result = run_longest_palindrome(Solution, s)
+result
+
+# %%
+assert_longest_palindrome(result, expected)
diff --git a/leetcode/longest_palindromic_substring/playground.ipynb b/leetcode/longest_palindromic_substring/playground.ipynb
deleted file mode 100644
index be01046..0000000
--- a/leetcode/longest_palindromic_substring/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_longest_palindrome, run_longest_palindrome\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "s = \"babad\"\n",
- "expected = {\"bab\", \"aba\"}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_longest_palindrome(Solution, s)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_longest_palindrome(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/longest_palindromic_substring/playground.py b/leetcode/longest_palindromic_substring/playground.py
new file mode 100644
index 0000000..ee1069b
--- /dev/null
+++ b/leetcode/longest_palindromic_substring/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_longest_palindrome, run_longest_palindrome
+from solution import Solution
+
+# %%
+# Example test case
+s = "babad"
+expected = {"bab", "aba"}
+
+# %%
+result = run_longest_palindrome(Solution, s)
+result
+
+# %%
+assert_longest_palindrome(result, expected)
diff --git a/leetcode/longest_palindromic_substring/test_solution.py b/leetcode/longest_palindromic_substring/test_solution.py
index 832e8fa..0cd1994 100644
--- a/leetcode/longest_palindromic_substring/test_solution.py
+++ b/leetcode/longest_palindromic_substring/test_solution.py
@@ -3,7 +3,7 @@
from leetcode_py.test_utils import logged_test
from .helpers import assert_longest_palindrome, run_longest_palindrome
-from .solution import Solution
+from .solution import Solution, SolutionManacher
class TestLongestPalindromicSubstring:
@@ -11,6 +11,7 @@ def setup_method(self):
self.solution = Solution()
@logged_test
+ @pytest.mark.parametrize("solution_class", [Solution, SolutionManacher])
@pytest.mark.parametrize(
"s, expected",
[
@@ -28,6 +29,6 @@ def setup_method(self):
("abcba", {"abcba"}),
],
)
- def test_longest_palindrome(self, s: str, expected: set[str]):
- result = run_longest_palindrome(Solution, s)
+ def test_longest_palindrome(self, solution_class: type, s: str, expected: set[str]):
+ result = run_longest_palindrome(solution_class, s)
assert_longest_palindrome(result, expected)
diff --git a/leetcode/longest_substring_without_repeating_characters/playground.ipynb b/leetcode/longest_substring_without_repeating_characters/playground.ipynb
deleted file mode 100644
index 0c23372..0000000
--- a/leetcode/longest_substring_without_repeating_characters/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_length_of_longest_substring, run_length_of_longest_substring\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "s = \"abcabcbb\"\n",
- "expected = 3"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_length_of_longest_substring(Solution, s)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_length_of_longest_substring(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/longest_substring_without_repeating_characters/playground.py b/leetcode/longest_substring_without_repeating_characters/playground.py
new file mode 100644
index 0000000..e2b9bf4
--- /dev/null
+++ b/leetcode/longest_substring_without_repeating_characters/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_length_of_longest_substring, run_length_of_longest_substring
+from solution import Solution
+
+# %%
+# Example test case
+s = "abcabcbb"
+expected = 3
+
+# %%
+result = run_length_of_longest_substring(Solution, s)
+result
+
+# %%
+assert_length_of_longest_substring(result, expected)
diff --git a/leetcode/lowest_common_ancestor_of_a_binary_search_tree/playground.ipynb b/leetcode/lowest_common_ancestor_of_a_binary_search_tree/playground.ipynb
deleted file mode 100644
index 6f5c85e..0000000
--- a/leetcode/lowest_common_ancestor_of_a_binary_search_tree/playground.ipynb
+++ /dev/null
@@ -1,72 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_lowest_common_ancestor, run_lowest_common_ancestor\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import TreeNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "root_list = [6, 2, 8, 0, 4, 7, 9, None, None, 3, 5]\n",
- "p_val = 2\n",
- "q_val = 8\n",
- "expected_val = 6"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_lowest_common_ancestor(Solution, root_list, p_val, q_val)\n",
- "result.val if result else None"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_lowest_common_ancestor(result, expected_val)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/lowest_common_ancestor_of_a_binary_search_tree/playground.py b/leetcode/lowest_common_ancestor_of_a_binary_search_tree/playground.py
new file mode 100644
index 0000000..1e7eb79
--- /dev/null
+++ b/leetcode/lowest_common_ancestor_of_a_binary_search_tree/playground.py
@@ -0,0 +1,32 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_lowest_common_ancestor, run_lowest_common_ancestor
+from solution import Solution
+
+# %%
+# Example test case
+root_list = [6, 2, 8, 0, 4, 7, 9, None, None, 3, 5]
+p_val = 2
+q_val = 8
+expected_val = 6
+
+# %%
+result = run_lowest_common_ancestor(Solution, root_list, p_val, q_val)
+result.val if result else None
+
+# %%
+assert_lowest_common_ancestor(result, expected_val)
diff --git a/leetcode/lowest_common_ancestor_of_a_binary_tree/playground.ipynb b/leetcode/lowest_common_ancestor_of_a_binary_tree/playground.ipynb
deleted file mode 100644
index 0251030..0000000
--- a/leetcode/lowest_common_ancestor_of_a_binary_tree/playground.ipynb
+++ /dev/null
@@ -1,72 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_lowest_common_ancestor, run_lowest_common_ancestor\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import TreeNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "root_list = [3, 5, 1, 6, 2, 0, 8, None, None, 7, 4]\n",
- "p_val = 5\n",
- "q_val = 1\n",
- "expected_val = 3"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_lowest_common_ancestor(Solution, root_list, p_val, q_val)\n",
- "result.val"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_lowest_common_ancestor(result, expected_val)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/lowest_common_ancestor_of_a_binary_tree/playground.py b/leetcode/lowest_common_ancestor_of_a_binary_tree/playground.py
new file mode 100644
index 0000000..39884fc
--- /dev/null
+++ b/leetcode/lowest_common_ancestor_of_a_binary_tree/playground.py
@@ -0,0 +1,32 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_lowest_common_ancestor, run_lowest_common_ancestor
+from solution import Solution
+
+# %%
+# Example test case
+root_list = [3, 5, 1, 6, 2, 0, 8, None, None, 7, 4]
+p_val = 5
+q_val = 1
+expected_val = 3
+
+# %%
+result = run_lowest_common_ancestor(Solution, root_list, p_val, q_val)
+result.val
+
+# %%
+assert_lowest_common_ancestor(result, expected_val)
diff --git a/leetcode/lru_cache/playground.ipynb b/leetcode/lru_cache/playground.ipynb
deleted file mode 100644
index 687c6f1..0000000
--- a/leetcode/lru_cache/playground.ipynb
+++ /dev/null
@@ -1,100 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_lru_cache, run_lru_cache\n",
- "from solution import LRUCache"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "operations = [\"LRUCache\", \"put\", \"put\", \"get\", \"put\", \"get\", \"put\", \"get\", \"get\", \"get\"]\n",
- "inputs = [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]\n",
- "expected = [None, None, None, 1, None, -1, None, -1, 3, 4]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "run",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "[None, None, None, 1, None, -1, None, -1, 3, 4]\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- "OrderedDict([(3, 3), (4, 4)])"
- ]
- },
- "execution_count": 5,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "result, cache = run_lru_cache(LRUCache, operations, inputs)\n",
- "print(result)\n",
- "cache.cache"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "assert",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "assert_lru_cache(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/lru_cache/playground.py b/leetcode/lru_cache/playground.py
new file mode 100644
index 0000000..6269d30
--- /dev/null
+++ b/leetcode/lru_cache/playground.py
@@ -0,0 +1,32 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_lru_cache, run_lru_cache
+from solution import LRUCache
+
+# %%
+# Example test case
+operations = ["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
+inputs = [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
+expected = [None, None, None, 1, None, -1, None, -1, 3, 4]
+
+# %%
+result, cache = run_lru_cache(LRUCache, operations, inputs)
+print(result)
+cache.cache
+
+# %%
+assert_lru_cache(result, expected)
diff --git a/leetcode/lru_cache/test_solution.py b/leetcode/lru_cache/test_solution.py
index 0fcaa3d..db70723 100644
--- a/leetcode/lru_cache/test_solution.py
+++ b/leetcode/lru_cache/test_solution.py
@@ -3,12 +3,13 @@
from leetcode_py.test_utils import logged_test
from .helpers import assert_lru_cache, run_lru_cache
-from .solution import LRUCache
+from .solution import LRUCache, LRUCacheWithDoublyList
class TestLRUCache:
@logged_test
+ @pytest.mark.parametrize("solution_class", [LRUCache, LRUCacheWithDoublyList])
@pytest.mark.parametrize(
"operations, inputs, expected",
[
@@ -66,6 +67,12 @@ class TestLRUCache:
),
],
)
- def test_lru_cache(self, operations: list[str], inputs: list[list[int]], expected: list[int | None]):
- result, _ = run_lru_cache(LRUCache, operations, inputs)
+ def test_lru_cache(
+ self,
+ solution_class: type,
+ operations: list[str],
+ inputs: list[list[int]],
+ expected: list[int | None],
+ ):
+ result, _ = run_lru_cache(solution_class, operations, inputs)
assert_lru_cache(result, expected)
diff --git a/leetcode/majority_element/playground.ipynb b/leetcode/majority_element/playground.ipynb
deleted file mode 100644
index 969279e..0000000
--- a/leetcode/majority_element/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_majority_element, run_majority_element\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "nums = [3, 2, 3]\n",
- "expected = 3"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_majority_element(Solution, nums)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_majority_element(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/majority_element/playground.py b/leetcode/majority_element/playground.py
new file mode 100644
index 0000000..ac2bcd3
--- /dev/null
+++ b/leetcode/majority_element/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_majority_element, run_majority_element
+from solution import Solution
+
+# %%
+# Example test case
+nums = [3, 2, 3]
+expected = 3
+
+# %%
+result = run_majority_element(Solution, nums)
+result
+
+# %%
+assert_majority_element(result, expected)
diff --git a/leetcode/maximum_depth_of_binary_tree/playground.ipynb b/leetcode/maximum_depth_of_binary_tree/playground.ipynb
deleted file mode 100644
index 53406b7..0000000
--- a/leetcode/maximum_depth_of_binary_tree/playground.ipynb
+++ /dev/null
@@ -1,70 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_max_depth, run_max_depth\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import TreeNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "root_list = [3, 9, 20, None, None, 15, 7]\n",
- "expected = 3"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_max_depth(Solution, root_list)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_max_depth(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/maximum_depth_of_binary_tree/playground.py b/leetcode/maximum_depth_of_binary_tree/playground.py
new file mode 100644
index 0000000..e2f6ebf
--- /dev/null
+++ b/leetcode/maximum_depth_of_binary_tree/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_max_depth, run_max_depth
+from solution import Solution
+
+# %%
+# Example test case
+root_list = [3, 9, 20, None, None, 15, 7]
+expected = 3
+
+# %%
+result = run_max_depth(Solution, root_list)
+result
+
+# %%
+assert_max_depth(result, expected)
diff --git a/leetcode/maximum_profit_in_job_scheduling/playground.ipynb b/leetcode/maximum_profit_in_job_scheduling/playground.ipynb
deleted file mode 100644
index cf2e659..0000000
--- a/leetcode/maximum_profit_in_job_scheduling/playground.ipynb
+++ /dev/null
@@ -1,70 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_job_scheduling, run_job_scheduling\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "start_time = [1, 2, 3, 3]\n",
- "end_time = [3, 4, 5, 6]\n",
- "profit = [50, 10, 40, 70]\n",
- "expected = 120"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_job_scheduling(Solution, start_time, end_time, profit)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_job_scheduling(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/maximum_profit_in_job_scheduling/playground.py b/leetcode/maximum_profit_in_job_scheduling/playground.py
new file mode 100644
index 0000000..777d927
--- /dev/null
+++ b/leetcode/maximum_profit_in_job_scheduling/playground.py
@@ -0,0 +1,32 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_job_scheduling, run_job_scheduling
+from solution import Solution
+
+# %%
+# Example test case
+start_time = [1, 2, 3, 3]
+end_time = [3, 4, 5, 6]
+profit = [50, 10, 40, 70]
+expected = 120
+
+# %%
+result = run_job_scheduling(Solution, start_time, end_time, profit)
+result
+
+# %%
+assert_job_scheduling(result, expected)
diff --git a/leetcode/maximum_subarray/playground.ipynb b/leetcode/maximum_subarray/playground.ipynb
deleted file mode 100644
index bd4f64e..0000000
--- a/leetcode/maximum_subarray/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_max_sub_array, run_max_sub_array\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]\n",
- "expected = 6"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_max_sub_array(Solution, nums)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_max_sub_array(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/maximum_subarray/playground.py b/leetcode/maximum_subarray/playground.py
new file mode 100644
index 0000000..4c514a9
--- /dev/null
+++ b/leetcode/maximum_subarray/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_max_sub_array, run_max_sub_array
+from solution import Solution
+
+# %%
+# Example test case
+nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
+expected = 6
+
+# %%
+result = run_max_sub_array(Solution, nums)
+result
+
+# %%
+assert_max_sub_array(result, expected)
diff --git a/leetcode/merge_intervals/playground.ipynb b/leetcode/merge_intervals/playground.ipynb
deleted file mode 100644
index f5c7c1e..0000000
--- a/leetcode/merge_intervals/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_merge, run_merge\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "intervals = [[1, 3], [2, 6], [8, 10], [15, 18]]\n",
- "expected = [[1, 6], [8, 10], [15, 18]]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_merge(Solution, intervals)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_merge(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/merge_intervals/playground.py b/leetcode/merge_intervals/playground.py
new file mode 100644
index 0000000..0c3c6b0
--- /dev/null
+++ b/leetcode/merge_intervals/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_merge, run_merge
+from solution import Solution
+
+# %%
+# Example test case
+intervals = [[1, 3], [2, 6], [8, 10], [15, 18]]
+expected = [[1, 6], [8, 10], [15, 18]]
+
+# %%
+result = run_merge(Solution, intervals)
+result
+
+# %%
+assert_merge(result, expected)
diff --git a/leetcode/merge_k_sorted_lists/playground.ipynb b/leetcode/merge_k_sorted_lists/playground.ipynb
deleted file mode 100644
index 44dff96..0000000
--- a/leetcode/merge_k_sorted_lists/playground.ipynb
+++ /dev/null
@@ -1,70 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_merge_k_lists, run_merge_k_lists\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import ListNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "lists_data = [[1, 4, 5], [1, 3, 4], [2, 6]]\n",
- "expected_data = [1, 1, 2, 3, 4, 4, 5, 6]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_merge_k_lists(Solution, lists_data)\n",
- "ListNode[int].to_list(result) if result else []"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_merge_k_lists(result, expected_data)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/merge_k_sorted_lists/playground.py b/leetcode/merge_k_sorted_lists/playground.py
new file mode 100644
index 0000000..f30a2ad
--- /dev/null
+++ b/leetcode/merge_k_sorted_lists/playground.py
@@ -0,0 +1,32 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_merge_k_lists, run_merge_k_lists
+from solution import Solution
+
+from leetcode_py import ListNode
+
+# %%
+# Example test case
+lists_data = [[1, 4, 5], [1, 3, 4], [2, 6]]
+expected_data = [1, 1, 2, 3, 4, 4, 5, 6]
+
+# %%
+result = run_merge_k_lists(Solution, lists_data)
+ListNode[int].to_list(result) if result else []
+
+# %%
+assert_merge_k_lists(result, expected_data)
diff --git a/leetcode/merge_two_sorted_lists/playground.ipynb b/leetcode/merge_two_sorted_lists/playground.ipynb
deleted file mode 100644
index 0ec2f71..0000000
--- a/leetcode/merge_two_sorted_lists/playground.ipynb
+++ /dev/null
@@ -1,71 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_merge_two_lists, run_merge_two_lists\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import ListNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "list1_vals = [1, 2, 4]\n",
- "list2_vals = [1, 3, 4]\n",
- "expected_vals = [1, 1, 2, 3, 4, 4]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_merge_two_lists(Solution, list1_vals, list2_vals)\n",
- "ListNode[int].to_list(result) if result else []"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_merge_two_lists(result, expected_vals)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/merge_two_sorted_lists/playground.py b/leetcode/merge_two_sorted_lists/playground.py
new file mode 100644
index 0000000..2d47603
--- /dev/null
+++ b/leetcode/merge_two_sorted_lists/playground.py
@@ -0,0 +1,33 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_merge_two_lists, run_merge_two_lists
+from solution import Solution
+
+from leetcode_py import ListNode
+
+# %%
+# Example test case
+list1_vals = [1, 2, 4]
+list2_vals = [1, 3, 4]
+expected_vals = [1, 1, 2, 3, 4, 4]
+
+# %%
+result = run_merge_two_lists(Solution, list1_vals, list2_vals)
+ListNode[int].to_list(result) if result else []
+
+# %%
+assert_merge_two_lists(result, expected_vals)
diff --git a/leetcode/middle_of_the_linked_list/playground.ipynb b/leetcode/middle_of_the_linked_list/playground.ipynb
deleted file mode 100644
index d078f51..0000000
--- a/leetcode/middle_of_the_linked_list/playground.ipynb
+++ /dev/null
@@ -1,70 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_middle_node, run_middle_node\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import ListNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "head_list = [1, 2, 3, 4, 5]\n",
- "expected_list = [3, 4, 5]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_middle_node(Solution, head_list)\n",
- "ListNode[int].to_list(result) if result else []"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_middle_node(result, expected_list)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/middle_of_the_linked_list/playground.py b/leetcode/middle_of_the_linked_list/playground.py
new file mode 100644
index 0000000..a81e5a1
--- /dev/null
+++ b/leetcode/middle_of_the_linked_list/playground.py
@@ -0,0 +1,32 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_middle_node, run_middle_node
+from solution import Solution
+
+from leetcode_py import ListNode
+
+# %%
+# Example test case
+head_list = [1, 2, 3, 4, 5]
+expected_list = [3, 4, 5]
+
+# %%
+result = run_middle_node(Solution, head_list)
+ListNode[int].to_list(result) if result else []
+
+# %%
+assert_middle_node(result, expected_list)
diff --git a/leetcode/min_stack/playground.ipynb b/leetcode/min_stack/playground.ipynb
deleted file mode 100644
index 0d24cc7..0000000
--- a/leetcode/min_stack/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_min_stack_operations, run_min_stack_operations\n",
- "from solution import MinStack"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "operations = [\"MinStack\", \"push\", \"push\", \"push\", \"getMin\", \"pop\", \"top\", \"getMin\"]\n",
- "inputs = [[], [-2], [0], [-3], [], [], [], []]\n",
- "expected = [None, None, None, None, -3, None, 0, -2]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_min_stack_operations(MinStack, operations, inputs)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_min_stack_operations(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/min_stack/playground.py b/leetcode/min_stack/playground.py
new file mode 100644
index 0000000..a331ae1
--- /dev/null
+++ b/leetcode/min_stack/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_min_stack_operations, run_min_stack_operations
+from solution import MinStack
+
+# %%
+# Example test case
+operations = ["MinStack", "push", "push", "push", "getMin", "pop", "top", "getMin"]
+inputs = [[], [-2], [0], [-3], [], [], [], []]
+expected = [None, None, None, None, -3, None, 0, -2]
+
+# %%
+result = run_min_stack_operations(MinStack, operations, inputs)
+result
+
+# %%
+assert_min_stack_operations(result, expected)
diff --git a/leetcode/minimum_height_trees/playground.ipynb b/leetcode/minimum_height_trees/playground.ipynb
deleted file mode 100644
index 8cf7734..0000000
--- a/leetcode/minimum_height_trees/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_find_min_height_trees, run_find_min_height_trees\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "n = 4\n",
- "edges = [[1, 0], [1, 2], [1, 3]]\n",
- "expected = [1]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_find_min_height_trees(Solution, n, edges)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_find_min_height_trees(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/minimum_height_trees/playground.py b/leetcode/minimum_height_trees/playground.py
new file mode 100644
index 0000000..32fdfbd
--- /dev/null
+++ b/leetcode/minimum_height_trees/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_find_min_height_trees, run_find_min_height_trees
+from solution import Solution
+
+# %%
+# Example test case
+n = 4
+edges = [[1, 0], [1, 2], [1, 3]]
+expected = [1]
+
+# %%
+result = run_find_min_height_trees(Solution, n, edges)
+result
+
+# %%
+assert_find_min_height_trees(result, expected)
diff --git a/leetcode/minimum_window_substring/playground.ipynb b/leetcode/minimum_window_substring/playground.ipynb
deleted file mode 100644
index c35e737..0000000
--- a/leetcode/minimum_window_substring/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_min_window, run_min_window\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "s = \"ADOBECODEBANC\"\n",
- "t = \"ABC\"\n",
- "expected = \"BANC\""
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_min_window(Solution, s, t)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_min_window(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/minimum_window_substring/playground.py b/leetcode/minimum_window_substring/playground.py
new file mode 100644
index 0000000..89b19b0
--- /dev/null
+++ b/leetcode/minimum_window_substring/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_min_window, run_min_window
+from solution import Solution
+
+# %%
+# Example test case
+s = "ADOBECODEBANC"
+t = "ABC"
+expected = "BANC"
+
+# %%
+result = run_min_window(Solution, s, t)
+result
+
+# %%
+assert_min_window(result, expected)
diff --git a/leetcode/number_of_islands/playground.ipynb b/leetcode/number_of_islands/playground.ipynb
deleted file mode 100644
index 57ebc3f..0000000
--- a/leetcode/number_of_islands/playground.ipynb
+++ /dev/null
@@ -1,73 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_num_islands, run_num_islands\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "grid = [\n",
- " [\"1\", \"1\", \"1\", \"1\", \"0\"],\n",
- " [\"1\", \"1\", \"0\", \"1\", \"0\"],\n",
- " [\"1\", \"1\", \"0\", \"0\", \"0\"],\n",
- " [\"0\", \"0\", \"0\", \"0\", \"0\"],\n",
- "]\n",
- "expected = 1"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_num_islands(Solution, grid)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_num_islands(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/number_of_islands/playground.py b/leetcode/number_of_islands/playground.py
new file mode 100644
index 0000000..8f12ebb
--- /dev/null
+++ b/leetcode/number_of_islands/playground.py
@@ -0,0 +1,35 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_num_islands, run_num_islands
+from solution import Solution
+
+# %%
+# Example test case
+grid = [
+ ["1", "1", "1", "1", "0"],
+ ["1", "1", "0", "1", "0"],
+ ["1", "1", "0", "0", "0"],
+ ["0", "0", "0", "0", "0"],
+]
+expected = 1
+
+# %%
+result = run_num_islands(Solution, grid)
+result
+
+# %%
+assert_num_islands(result, expected)
diff --git a/leetcode/partition_equal_subset_sum/playground.ipynb b/leetcode/partition_equal_subset_sum/playground.ipynb
deleted file mode 100644
index 5597f66..0000000
--- a/leetcode/partition_equal_subset_sum/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_can_partition, run_can_partition\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "nums = [1, 5, 11, 5]\n",
- "expected = True"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_can_partition(Solution, nums)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_can_partition(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/partition_equal_subset_sum/playground.py b/leetcode/partition_equal_subset_sum/playground.py
new file mode 100644
index 0000000..9bf8361
--- /dev/null
+++ b/leetcode/partition_equal_subset_sum/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_can_partition, run_can_partition
+from solution import Solution
+
+# %%
+# Example test case
+nums = [1, 5, 11, 5]
+expected = True
+
+# %%
+result = run_can_partition(Solution, nums)
+result
+
+# %%
+assert_can_partition(result, expected)
diff --git a/leetcode/partition_equal_subset_sum/test_solution.py b/leetcode/partition_equal_subset_sum/test_solution.py
index e826127..a480df5 100644
--- a/leetcode/partition_equal_subset_sum/test_solution.py
+++ b/leetcode/partition_equal_subset_sum/test_solution.py
@@ -3,7 +3,7 @@
from leetcode_py.test_utils import logged_test
from .helpers import assert_can_partition, run_can_partition
-from .solution import Solution
+from .solution import Solution, SolutionBitset
class TestPartitionEqualSubsetSum:
@@ -11,6 +11,7 @@ def setup_method(self):
self.solution = Solution()
@logged_test
+ @pytest.mark.parametrize("solution_class", [Solution, SolutionBitset])
@pytest.mark.parametrize(
"nums, expected",
[
@@ -31,6 +32,6 @@ def setup_method(self):
([4, 4, 4, 4, 4, 4], True),
],
)
- def test_can_partition(self, nums: list[int], expected: bool):
- result = run_can_partition(Solution, nums)
+ def test_can_partition(self, solution_class: type, nums: list[int], expected: bool):
+ result = run_can_partition(solution_class, nums)
assert_can_partition(result, expected)
diff --git a/leetcode/permutations/playground.ipynb b/leetcode/permutations/playground.ipynb
deleted file mode 100644
index 3df2f29..0000000
--- a/leetcode/permutations/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_permute, run_permute\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "nums = [1, 2, 3]\n",
- "expected = [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_permute(Solution, nums)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_permute(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/permutations/playground.py b/leetcode/permutations/playground.py
new file mode 100644
index 0000000..07de7dd
--- /dev/null
+++ b/leetcode/permutations/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_permute, run_permute
+from solution import Solution
+
+# %%
+# Example test case
+nums = [1, 2, 3]
+expected = [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
+
+# %%
+result = run_permute(Solution, nums)
+result
+
+# %%
+assert_permute(result, expected)
diff --git a/leetcode/product_of_array_except_self/playground.ipynb b/leetcode/product_of_array_except_self/playground.ipynb
deleted file mode 100644
index c96f1f3..0000000
--- a/leetcode/product_of_array_except_self/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_product_except_self, run_product_except_self\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "nums = [1, 2, 3, 4]\n",
- "expected = [24, 12, 8, 6]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_product_except_self(Solution, nums)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_product_except_self(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/product_of_array_except_self/playground.py b/leetcode/product_of_array_except_self/playground.py
new file mode 100644
index 0000000..f8a43ad
--- /dev/null
+++ b/leetcode/product_of_array_except_self/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_product_except_self, run_product_except_self
+from solution import Solution
+
+# %%
+# Example test case
+nums = [1, 2, 3, 4]
+expected = [24, 12, 8, 6]
+
+# %%
+result = run_product_except_self(Solution, nums)
+result
+
+# %%
+assert_product_except_self(result, expected)
diff --git a/leetcode/ransom_note/playground.ipynb b/leetcode/ransom_note/playground.ipynb
deleted file mode 100644
index 5982636..0000000
--- a/leetcode/ransom_note/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_can_construct, run_can_construct\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "ransom_note = \"aa\"\n",
- "magazine = \"aab\"\n",
- "expected = True"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_can_construct(Solution, ransom_note, magazine)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_can_construct(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/ransom_note/playground.py b/leetcode/ransom_note/playground.py
new file mode 100644
index 0000000..86cbb15
--- /dev/null
+++ b/leetcode/ransom_note/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_can_construct, run_can_construct
+from solution import Solution
+
+# %%
+# Example test case
+ransom_note = "aa"
+magazine = "aab"
+expected = True
+
+# %%
+result = run_can_construct(Solution, ransom_note, magazine)
+result
+
+# %%
+assert_can_construct(result, expected)
diff --git a/leetcode/reverse_linked_list/playground.ipynb b/leetcode/reverse_linked_list/playground.ipynb
deleted file mode 100644
index b6a4171..0000000
--- a/leetcode/reverse_linked_list/playground.ipynb
+++ /dev/null
@@ -1,70 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_reverse_list, run_reverse_list\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import ListNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "head_list = [1, 2, 3, 4, 5]\n",
- "expected_list = [5, 4, 3, 2, 1]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_reverse_list(Solution, head_list)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_reverse_list(result, expected_list)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/reverse_linked_list/playground.py b/leetcode/reverse_linked_list/playground.py
new file mode 100644
index 0000000..375048c
--- /dev/null
+++ b/leetcode/reverse_linked_list/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_reverse_list, run_reverse_list
+from solution import Solution
+
+# %%
+# Example test case
+head_list = [1, 2, 3, 4, 5]
+expected_list = [5, 4, 3, 2, 1]
+
+# %%
+result = run_reverse_list(Solution, head_list)
+result
+
+# %%
+assert_reverse_list(result, expected_list)
diff --git a/leetcode/reverse_linked_list_ii/playground.ipynb b/leetcode/reverse_linked_list_ii/playground.ipynb
deleted file mode 100644
index 0cd88a4..0000000
--- a/leetcode/reverse_linked_list_ii/playground.ipynb
+++ /dev/null
@@ -1,71 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_reverse_between, run_reverse_between\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import ListNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "head_list = [1, 2, 3, 4, 5]\n",
- "left, right = 2, 4\n",
- "expected_list = [1, 4, 3, 2, 5]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_reverse_between(Solution, head_list, left, right)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_reverse_between(result, expected_list)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/reverse_linked_list_ii/playground.py b/leetcode/reverse_linked_list_ii/playground.py
new file mode 100644
index 0000000..0ee9f36
--- /dev/null
+++ b/leetcode/reverse_linked_list_ii/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_reverse_between, run_reverse_between
+from solution import Solution
+
+# %%
+# Example test case
+head_list = [1, 2, 3, 4, 5]
+left, right = 2, 4
+expected_list = [1, 4, 3, 2, 5]
+
+# %%
+result = run_reverse_between(Solution, head_list, left, right)
+result
+
+# %%
+assert_reverse_between(result, expected_list)
diff --git a/leetcode/rotting_oranges/playground.ipynb b/leetcode/rotting_oranges/playground.ipynb
deleted file mode 100644
index fde0cf1..0000000
--- a/leetcode/rotting_oranges/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_oranges_rotting, run_oranges_rotting\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "grid = [[2, 1, 1], [1, 1, 0], [0, 1, 1]]\n",
- "expected = 4"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_oranges_rotting(Solution, grid)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_oranges_rotting(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/rotting_oranges/playground.py b/leetcode/rotting_oranges/playground.py
new file mode 100644
index 0000000..7f1f44e
--- /dev/null
+++ b/leetcode/rotting_oranges/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_oranges_rotting, run_oranges_rotting
+from solution import Solution
+
+# %%
+# Example test case
+grid = [[2, 1, 1], [1, 1, 0], [0, 1, 1]]
+expected = 4
+
+# %%
+result = run_oranges_rotting(Solution, grid)
+result
+
+# %%
+assert_oranges_rotting(result, expected)
diff --git a/leetcode/search_in_rotated_sorted_array/playground.ipynb b/leetcode/search_in_rotated_sorted_array/playground.ipynb
deleted file mode 100644
index 6dadf29..0000000
--- a/leetcode/search_in_rotated_sorted_array/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_search, run_search\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "nums = [4, 5, 6, 7, 0, 1, 2]\n",
- "target = 0\n",
- "expected = 4"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_search(Solution, nums, target)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_search(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/search_in_rotated_sorted_array/playground.py b/leetcode/search_in_rotated_sorted_array/playground.py
new file mode 100644
index 0000000..1c72b5a
--- /dev/null
+++ b/leetcode/search_in_rotated_sorted_array/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_search, run_search
+from solution import Solution
+
+# %%
+# Example test case
+nums = [4, 5, 6, 7, 0, 1, 2]
+target = 0
+expected = 4
+
+# %%
+result = run_search(Solution, nums, target)
+result
+
+# %%
+assert_search(result, expected)
diff --git a/leetcode/serialize_and_deserialize_binary_tree/playground.ipynb b/leetcode/serialize_and_deserialize_binary_tree/playground.ipynb
deleted file mode 100644
index a1b0bf6..0000000
--- a/leetcode/serialize_and_deserialize_binary_tree/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_serialize_deserialize, run_serialize_deserialize\n",
- "from solution import Codec\n",
- "\n",
- "from leetcode_py import TreeNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "root_list = [1, 2, 3, None, None, 4, 5]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_serialize_deserialize(Codec, root_list)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_serialize_deserialize(result, root_list)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/serialize_and_deserialize_binary_tree/playground.py b/leetcode/serialize_and_deserialize_binary_tree/playground.py
new file mode 100644
index 0000000..06cf1af
--- /dev/null
+++ b/leetcode/serialize_and_deserialize_binary_tree/playground.py
@@ -0,0 +1,29 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_serialize_deserialize, run_serialize_deserialize
+from solution import Codec
+
+# %%
+# Example test case
+root_list = [1, 2, 3, None, None, 4, 5]
+
+# %%
+result = run_serialize_deserialize(Codec, root_list)
+result
+
+# %%
+assert_serialize_deserialize(result, root_list)
diff --git a/leetcode/sort_colors/playground.ipynb b/leetcode/sort_colors/playground.ipynb
deleted file mode 100644
index b2f099f..0000000
--- a/leetcode/sort_colors/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_sort_colors, run_sort_colors\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "nums = [2, 0, 2, 1, 1, 0]\n",
- "expected = [0, 0, 1, 1, 2, 2]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_sort_colors(Solution, nums)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_sort_colors(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/sort_colors/playground.py b/leetcode/sort_colors/playground.py
new file mode 100644
index 0000000..9b2c154
--- /dev/null
+++ b/leetcode/sort_colors/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_sort_colors, run_sort_colors
+from solution import Solution
+
+# %%
+# Example test case
+nums = [2, 0, 2, 1, 1, 0]
+expected = [0, 0, 1, 1, 2, 2]
+
+# %%
+result = run_sort_colors(Solution, nums)
+result
+
+# %%
+assert_sort_colors(result, expected)
diff --git a/leetcode/spiral_matrix/playground.ipynb b/leetcode/spiral_matrix/playground.ipynb
deleted file mode 100644
index 0668a7e..0000000
--- a/leetcode/spiral_matrix/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_spiral_order, run_spiral_order\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]\n",
- "expected = [1, 2, 3, 6, 9, 8, 7, 4, 5]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_spiral_order(Solution, matrix)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_spiral_order(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/spiral_matrix/playground.py b/leetcode/spiral_matrix/playground.py
new file mode 100644
index 0000000..07f54ab
--- /dev/null
+++ b/leetcode/spiral_matrix/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_spiral_order, run_spiral_order
+from solution import Solution
+
+# %%
+# Example test case
+matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
+expected = [1, 2, 3, 6, 9, 8, 7, 4, 5]
+
+# %%
+result = run_spiral_order(Solution, matrix)
+result
+
+# %%
+assert_spiral_order(result, expected)
diff --git a/leetcode/string_to_integer_atoi/playground.ipynb b/leetcode/string_to_integer_atoi/playground.ipynb
deleted file mode 100644
index 82ecfb7..0000000
--- a/leetcode/string_to_integer_atoi/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_my_atoi, run_my_atoi\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "s = \"42\"\n",
- "expected = 42"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_my_atoi(Solution, s)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_my_atoi(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/string_to_integer_atoi/playground.py b/leetcode/string_to_integer_atoi/playground.py
new file mode 100644
index 0000000..c0656aa
--- /dev/null
+++ b/leetcode/string_to_integer_atoi/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_my_atoi, run_my_atoi
+from solution import Solution
+
+# %%
+# Example test case
+s = "42"
+expected = 42
+
+# %%
+result = run_my_atoi(Solution, s)
+result
+
+# %%
+assert_my_atoi(result, expected)
diff --git a/leetcode/task_scheduler/playground.ipynb b/leetcode/task_scheduler/playground.ipynb
deleted file mode 100644
index 3ad7d83..0000000
--- a/leetcode/task_scheduler/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_least_interval, run_least_interval\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "tasks = [\"A\", \"A\", \"A\", \"B\", \"B\", \"B\"]\n",
- "n = 2\n",
- "expected = 8"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_least_interval(Solution, tasks, n)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_least_interval(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/task_scheduler/playground.py b/leetcode/task_scheduler/playground.py
new file mode 100644
index 0000000..e0bcc11
--- /dev/null
+++ b/leetcode/task_scheduler/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_least_interval, run_least_interval
+from solution import Solution
+
+# %%
+# Example test case
+tasks = ["A", "A", "A", "B", "B", "B"]
+n = 2
+expected = 8
+
+# %%
+result = run_least_interval(Solution, tasks, n)
+result
+
+# %%
+assert_least_interval(result, expected)
diff --git a/leetcode/three_sum/playground.ipynb b/leetcode/three_sum/playground.ipynb
deleted file mode 100644
index ddbc41b..0000000
--- a/leetcode/three_sum/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_three_sum, run_three_sum\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "nums = [-1, 0, 1, 2, -1, -4]\n",
- "expected = [[-1, -1, 2], [-1, 0, 1]]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_three_sum(Solution, nums)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_three_sum(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/three_sum/playground.py b/leetcode/three_sum/playground.py
new file mode 100644
index 0000000..818263e
--- /dev/null
+++ b/leetcode/three_sum/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_three_sum, run_three_sum
+from solution import Solution
+
+# %%
+# Example test case
+nums = [-1, 0, 1, 2, -1, -4]
+expected = [[-1, -1, 2], [-1, 0, 1]]
+
+# %%
+result = run_three_sum(Solution, nums)
+result
+
+# %%
+assert_three_sum(result, expected)
diff --git a/leetcode/time_based_key_value_store/playground.ipynb b/leetcode/time_based_key_value_store/playground.ipynb
deleted file mode 100644
index 28f407a..0000000
--- a/leetcode/time_based_key_value_store/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_time_map_operations, run_time_map_operations\n",
- "from solution import TimeMap"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "operations = [\"TimeMap\", \"set\", \"get\", \"get\", \"set\", \"get\", \"get\"]\n",
- "inputs = [[], [\"foo\", \"bar\", 1], [\"foo\", 1], [\"foo\", 3], [\"foo\", \"bar2\", 4], [\"foo\", 4], [\"foo\", 5]]\n",
- "expected = [None, None, \"bar\", \"bar\", None, \"bar2\", \"bar2\"]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_time_map_operations(TimeMap, operations, inputs)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_time_map_operations(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/time_based_key_value_store/playground.py b/leetcode/time_based_key_value_store/playground.py
new file mode 100644
index 0000000..ead5892
--- /dev/null
+++ b/leetcode/time_based_key_value_store/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_time_map_operations, run_time_map_operations
+from solution import TimeMap
+
+# %%
+# Example test case
+operations = ["TimeMap", "set", "get", "get", "set", "get", "get"]
+inputs = [[], ["foo", "bar", 1], ["foo", 1], ["foo", 3], ["foo", "bar2", 4], ["foo", 4], ["foo", 5]]
+expected = [None, None, "bar", "bar", None, "bar2", "bar2"]
+
+# %%
+result = run_time_map_operations(TimeMap, operations, inputs)
+result
+
+# %%
+assert_time_map_operations(result, expected)
diff --git a/leetcode/trapping_rain_water/playground.ipynb b/leetcode/trapping_rain_water/playground.ipynb
deleted file mode 100644
index b4db37d..0000000
--- a/leetcode/trapping_rain_water/playground.ipynb
+++ /dev/null
@@ -1,91 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_trap, run_trap\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]\n",
- "expected = 6"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "run",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "6"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "result = run_trap(Solution, height)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "assert",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "assert_trap(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/trapping_rain_water/playground.py b/leetcode/trapping_rain_water/playground.py
new file mode 100644
index 0000000..749b303
--- /dev/null
+++ b/leetcode/trapping_rain_water/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_trap, run_trap
+from solution import Solution
+
+# %%
+# Example test case
+height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
+expected = 6
+
+# %%
+result = run_trap(Solution, height)
+result
+
+# %%
+assert_trap(result, expected)
diff --git a/leetcode/two_sum/playground.ipynb b/leetcode/two_sum/playground.ipynb
deleted file mode 100644
index 1a6ec8b..0000000
--- a/leetcode/two_sum/playground.ipynb
+++ /dev/null
@@ -1,92 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_two_sum, run_two_sum\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "nums = [2, 7, 11, 15]\n",
- "target = 9\n",
- "expected = [0, 1]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "run",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "[0, 1]"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "result = run_two_sum(Solution, nums, target)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "assert",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "assert_two_sum(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/two_sum/playground.py b/leetcode/two_sum/playground.py
new file mode 100644
index 0000000..79ef5d4
--- /dev/null
+++ b/leetcode/two_sum/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_two_sum, run_two_sum
+from solution import Solution
+
+# %%
+# Example test case
+nums = [2, 7, 11, 15]
+target = 9
+expected = [0, 1]
+
+# %%
+result = run_two_sum(Solution, nums, target)
+result
+
+# %%
+assert_two_sum(result, expected)
diff --git a/leetcode/valid_anagram/playground.ipynb b/leetcode/valid_anagram/playground.ipynb
deleted file mode 100644
index c178ebe..0000000
--- a/leetcode/valid_anagram/playground.ipynb
+++ /dev/null
@@ -1,92 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_is_anagram, run_is_anagram\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "s = \"anagram\"\n",
- "t = \"nagaram\"\n",
- "expected = True"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "run",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "result = run_is_anagram(Solution, s, t)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "assert",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "assert_is_anagram(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/valid_anagram/playground.py b/leetcode/valid_anagram/playground.py
new file mode 100644
index 0000000..46bfe1d
--- /dev/null
+++ b/leetcode/valid_anagram/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_is_anagram, run_is_anagram
+from solution import Solution
+
+# %%
+# Example test case
+s = "anagram"
+t = "nagaram"
+expected = True
+
+# %%
+result = run_is_anagram(Solution, s, t)
+result
+
+# %%
+assert_is_anagram(result, expected)
diff --git a/leetcode/valid_palindrome/playground.ipynb b/leetcode/valid_palindrome/playground.ipynb
deleted file mode 100644
index c4c6bbc..0000000
--- a/leetcode/valid_palindrome/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_is_palindrome, run_is_palindrome\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "s = \"A man, a plan, a canal: Panama\"\n",
- "expected = True"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_is_palindrome(Solution, s)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_is_palindrome(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/valid_palindrome/playground.py b/leetcode/valid_palindrome/playground.py
new file mode 100644
index 0000000..64924c5
--- /dev/null
+++ b/leetcode/valid_palindrome/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_is_palindrome, run_is_palindrome
+from solution import Solution
+
+# %%
+# Example test case
+s = "A man, a plan, a canal: Panama"
+expected = True
+
+# %%
+result = run_is_palindrome(Solution, s)
+result
+
+# %%
+assert_is_palindrome(result, expected)
diff --git a/leetcode/valid_parentheses/playground.ipynb b/leetcode/valid_parentheses/playground.ipynb
deleted file mode 100644
index e5324f0..0000000
--- a/leetcode/valid_parentheses/playground.ipynb
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_is_valid, run_is_valid\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "s = \"()\"\n",
- "expected = True"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_is_valid(Solution, s)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_is_valid(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/valid_parentheses/playground.py b/leetcode/valid_parentheses/playground.py
new file mode 100644
index 0000000..7c64862
--- /dev/null
+++ b/leetcode/valid_parentheses/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_is_valid, run_is_valid
+from solution import Solution
+
+# %%
+# Example test case
+s = "()"
+expected = True
+
+# %%
+result = run_is_valid(Solution, s)
+result
+
+# %%
+assert_is_valid(result, expected)
diff --git a/leetcode/validate_binary_search_tree/playground.ipynb b/leetcode/validate_binary_search_tree/playground.ipynb
deleted file mode 100644
index a53e0f9..0000000
--- a/leetcode/validate_binary_search_tree/playground.ipynb
+++ /dev/null
@@ -1,70 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_is_valid_bst, run_is_valid_bst\n",
- "from solution import Solution\n",
- "\n",
- "from leetcode_py import TreeNode"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "root_list = [2, 1, 3]\n",
- "expected = True"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_is_valid_bst(Solution, root_list)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_is_valid_bst(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/validate_binary_search_tree/playground.py b/leetcode/validate_binary_search_tree/playground.py
new file mode 100644
index 0000000..bd7fd3c
--- /dev/null
+++ b/leetcode/validate_binary_search_tree/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_is_valid_bst, run_is_valid_bst
+from solution import Solution
+
+# %%
+# Example test case
+root_list = [2, 1, 3]
+expected = True
+
+# %%
+result = run_is_valid_bst(Solution, root_list)
+result
+
+# %%
+assert_is_valid_bst(result, expected)
diff --git a/leetcode/validate_binary_search_tree/test_solution.py b/leetcode/validate_binary_search_tree/test_solution.py
index 8e5eca0..2491911 100644
--- a/leetcode/validate_binary_search_tree/test_solution.py
+++ b/leetcode/validate_binary_search_tree/test_solution.py
@@ -3,7 +3,7 @@
from leetcode_py.test_utils import logged_test
from .helpers import assert_is_valid_bst, run_is_valid_bst
-from .solution import Solution
+from .solution import Solution, SolutionBFS, SolutionDFS
class TestValidateBinarySearchTree:
@@ -11,6 +11,7 @@ def setup_method(self):
self.solution = Solution()
@logged_test
+ @pytest.mark.parametrize("solution_class", [Solution, SolutionDFS, SolutionBFS])
@pytest.mark.parametrize(
"root_list, expected",
[
@@ -28,6 +29,6 @@ def setup_method(self):
([3, 1, 5, 0, 2, 4, 6], True),
],
)
- def test_is_valid_bst(self, root_list: list[int | None], expected: bool):
- result = run_is_valid_bst(Solution, root_list)
+ def test_is_valid_bst(self, solution_class: type, root_list: list[int | None], expected: bool):
+ result = run_is_valid_bst(solution_class, root_list)
assert_is_valid_bst(result, expected)
diff --git a/leetcode/word_break/playground.ipynb b/leetcode/word_break/playground.ipynb
deleted file mode 100644
index b606647..0000000
--- a/leetcode/word_break/playground.ipynb
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_word_break, run_word_break\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "s = \"leetcode\"\n",
- "word_dict = [\"leet\", \"code\"]\n",
- "expected = True"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_word_break(Solution, s, word_dict)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_word_break(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/word_break/playground.py b/leetcode/word_break/playground.py
new file mode 100644
index 0000000..4ee7af7
--- /dev/null
+++ b/leetcode/word_break/playground.py
@@ -0,0 +1,31 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_word_break, run_word_break
+from solution import Solution
+
+# %%
+# Example test case
+s = "leetcode"
+word_dict = ["leet", "code"]
+expected = True
+
+# %%
+result = run_word_break(Solution, s, word_dict)
+result
+
+# %%
+assert_word_break(result, expected)
diff --git a/leetcode/word_ladder/playground.ipynb b/leetcode/word_ladder/playground.ipynb
deleted file mode 100644
index 863eedf..0000000
--- a/leetcode/word_ladder/playground.ipynb
+++ /dev/null
@@ -1,70 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_ladder_length, run_ladder_length\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "begin_word = \"hit\"\n",
- "end_word = \"cog\"\n",
- "word_list = [\"hot\", \"dot\", \"dog\", \"lot\", \"log\", \"cog\"]\n",
- "expected = 5"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "run",
- "metadata": {},
- "outputs": [],
- "source": [
- "result = run_ladder_length(Solution, begin_word, end_word, word_list)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "assert",
- "metadata": {},
- "outputs": [],
- "source": [
- "assert_ladder_length(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/word_ladder/playground.py b/leetcode/word_ladder/playground.py
new file mode 100644
index 0000000..a1dabdc
--- /dev/null
+++ b/leetcode/word_ladder/playground.py
@@ -0,0 +1,32 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_ladder_length, run_ladder_length
+from solution import Solution
+
+# %%
+# Example test case
+begin_word = "hit"
+end_word = "cog"
+word_list = ["hot", "dot", "dog", "lot", "log", "cog"]
+expected = 5
+
+# %%
+result = run_ladder_length(Solution, begin_word, end_word, word_list)
+result
+
+# %%
+assert_ladder_length(result, expected)
diff --git a/leetcode/zero_one_matrix/playground.ipynb b/leetcode/zero_one_matrix/playground.ipynb
deleted file mode 100644
index 6b3b709..0000000
--- a/leetcode/zero_one_matrix/playground.ipynb
+++ /dev/null
@@ -1,91 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "imports",
- "metadata": {},
- "outputs": [],
- "source": [
- "from helpers import assert_update_matrix, run_update_matrix\n",
- "from solution import Solution"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "setup",
- "metadata": {},
- "outputs": [],
- "source": [
- "# Example test case\n",
- "mat = [[0, 0, 0], [0, 1, 0], [1, 1, 1]]\n",
- "expected = [[0, 0, 0], [0, 1, 0], [1, 2, 1]]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "run",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "[[0, 0, 0], [0, 1, 0], [1, 2, 1]]"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "result = run_update_matrix(Solution, mat)\n",
- "result"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "assert",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "True"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "assert_update_matrix(result, expected)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "leetcode-py-py3.13",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.13.7"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
diff --git a/leetcode/zero_one_matrix/playground.py b/leetcode/zero_one_matrix/playground.py
new file mode 100644
index 0000000..a727bed
--- /dev/null
+++ b/leetcode/zero_one_matrix/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# formats: ipynb,py:percent
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_update_matrix, run_update_matrix
+from solution import Solution
+
+# %%
+# Example test case
+mat = [[0, 0, 0], [0, 1, 0], [1, 1, 1]]
+expected = [[0, 0, 0], [0, 1, 0], [1, 2, 1]]
+
+# %%
+result = run_update_matrix(Solution, mat)
+result
+
+# %%
+assert_update_matrix(result, expected)
diff --git a/poetry.lock b/poetry.lock
index 54bb124..9f49b72 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -61,6 +61,26 @@ files = [
astroid = ["astroid (>=2,<4)"]
test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"]
+[[package]]
+name = "attrs"
+version = "25.3.0"
+description = "Classes Without Boilerplate"
+optional = false
+python-versions = ">=3.8"
+groups = ["dev"]
+files = [
+ {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"},
+ {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"},
+]
+
+[package.extras]
+benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
+cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
+dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
+docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"]
+tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
+tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""]
+
[[package]]
name = "autopep8"
version = "2.3.2"
@@ -600,6 +620,21 @@ files = [
[package.extras]
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""]
+[[package]]
+name = "fastjsonschema"
+version = "2.21.2"
+description = "Fastest Python implementation of JSON schema"
+optional = false
+python-versions = "*"
+groups = ["dev"]
+files = [
+ {file = "fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463"},
+ {file = "fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de"},
+]
+
+[package.extras]
+devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"]
+
[[package]]
name = "filelock"
version = "3.19.1"
@@ -806,6 +841,43 @@ MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
+[[package]]
+name = "jsonschema"
+version = "4.25.1"
+description = "An implementation of JSON Schema validation for Python"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63"},
+ {file = "jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85"},
+]
+
+[package.dependencies]
+attrs = ">=22.2.0"
+jsonschema-specifications = ">=2023.03.6"
+referencing = ">=0.28.4"
+rpds-py = ">=0.7.1"
+
+[package.extras]
+format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"]
+format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "rfc3987-syntax (>=1.1.0)", "uri-template", "webcolors (>=24.6.0)"]
+
+[[package]]
+name = "jsonschema-specifications"
+version = "2025.9.1"
+description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe"},
+ {file = "jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d"},
+]
+
+[package.dependencies]
+referencing = ">=0.31.0"
+
[[package]]
name = "jupyter-client"
version = "8.6.3"
@@ -850,6 +922,35 @@ traitlets = ">=5.3"
docs = ["intersphinx-registry", "myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-spelling", "traitlets"]
test = ["ipykernel", "pre-commit", "pytest (<9)", "pytest-cov", "pytest-timeout"]
+[[package]]
+name = "jupytext"
+version = "1.17.3"
+description = "Jupyter notebooks as Markdown documents, Julia, Python or R scripts"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "jupytext-1.17.3-py3-none-any.whl", hash = "sha256:09b0a94cd904416e823a5ba9f41bd181031215b6fc682d2b5c18e68354feb17c"},
+ {file = "jupytext-1.17.3.tar.gz", hash = "sha256:8b6dae76d63c95cad47b493c38f0d9c74491fb621dcd0980abfcac4c8f168679"},
+]
+
+[package.dependencies]
+markdown-it-py = ">=1.0"
+mdit-py-plugins = "*"
+nbformat = "*"
+packaging = "*"
+pyyaml = "*"
+
+[package.extras]
+dev = ["autopep8", "black", "flake8", "gitpython", "ipykernel", "isort", "jupyter-fs[fs] (>=1.0)", "jupyter-server (!=2.11)", "nbconvert", "pre-commit", "pytest", "pytest-asyncio", "pytest-cov (>=2.6.1)", "pytest-randomly", "pytest-xdist", "sphinx", "sphinx-gallery (>=0.8)"]
+docs = ["myst-parser", "sphinx", "sphinx-copybutton", "sphinx-rtd-theme"]
+test = ["pytest", "pytest-asyncio", "pytest-randomly", "pytest-xdist"]
+test-cov = ["black", "ipykernel", "jupyter-server (!=2.11)", "nbconvert", "pytest", "pytest-asyncio", "pytest-cov (>=2.6.1)", "pytest-randomly", "pytest-xdist"]
+test-external = ["autopep8", "black", "flake8", "gitpython", "ipykernel", "isort", "jupyter-fs[fs] (>=1.0)", "jupyter-server (!=2.11)", "nbconvert", "pre-commit", "pytest", "pytest-asyncio", "pytest-randomly", "pytest-xdist", "sphinx", "sphinx-gallery (>=0.8)"]
+test-functional = ["black", "pytest", "pytest-asyncio", "pytest-randomly", "pytest-xdist"]
+test-integration = ["black", "ipykernel", "jupyter-server (!=2.11)", "nbconvert", "pytest", "pytest-asyncio", "pytest-randomly", "pytest-xdist"]
+test-ui = ["bash-kernel"]
+
[[package]]
name = "loguru"
version = "0.7.3"
@@ -979,6 +1080,26 @@ files = [
[package.dependencies]
traitlets = "*"
+[[package]]
+name = "mdit-py-plugins"
+version = "0.5.0"
+description = "Collection of plugins for markdown-it-py"
+optional = false
+python-versions = ">=3.10"
+groups = ["dev"]
+files = [
+ {file = "mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f"},
+ {file = "mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6"},
+]
+
+[package.dependencies]
+markdown-it-py = ">=2.0.0,<5.0.0"
+
+[package.extras]
+code-style = ["pre-commit"]
+rtd = ["myst-parser", "sphinx-book-theme"]
+testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
+
[[package]]
name = "mdurl"
version = "0.1.2"
@@ -1063,6 +1184,28 @@ files = [
{file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"},
]
+[[package]]
+name = "nbformat"
+version = "5.10.4"
+description = "The Jupyter Notebook format"
+optional = false
+python-versions = ">=3.8"
+groups = ["dev"]
+files = [
+ {file = "nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b"},
+ {file = "nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a"},
+]
+
+[package.dependencies]
+fastjsonschema = ">=2.15"
+jsonschema = ">=2.6"
+jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0"
+traitlets = ">=5.1"
+
+[package.extras]
+docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"]
+test = ["pep440", "pre-commit", "pytest", "testpath"]
+
[[package]]
name = "nbqa"
version = "1.9.1"
@@ -1597,6 +1740,22 @@ files = [
[package.dependencies]
cffi = {version = "*", markers = "implementation_name == \"pypy\""}
+[[package]]
+name = "referencing"
+version = "0.36.2"
+description = "JSON Referencing + Python"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"},
+ {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"},
+]
+
+[package.dependencies]
+attrs = ">=22.2.0"
+rpds-py = ">=0.7.0"
+
[[package]]
name = "requests"
version = "2.32.5"
@@ -1638,6 +1797,171 @@ pygments = ">=2.13.0,<3.0.0"
[package.extras]
jupyter = ["ipywidgets (>=7.5.1,<9)"]
+[[package]]
+name = "rpds-py"
+version = "0.27.1"
+description = "Python bindings to Rust's persistent data structures (rpds)"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+ {file = "rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef"},
+ {file = "rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be"},
+ {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61"},
+ {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb"},
+ {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657"},
+ {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013"},
+ {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a"},
+ {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1"},
+ {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10"},
+ {file = "rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808"},
+ {file = "rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8"},
+ {file = "rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9"},
+ {file = "rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4"},
+ {file = "rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1"},
+ {file = "rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881"},
+ {file = "rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5"},
+ {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e"},
+ {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c"},
+ {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195"},
+ {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52"},
+ {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed"},
+ {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a"},
+ {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde"},
+ {file = "rpds_py-0.27.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21"},
+ {file = "rpds_py-0.27.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9"},
+ {file = "rpds_py-0.27.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948"},
+ {file = "rpds_py-0.27.1-cp311-cp311-win32.whl", hash = "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39"},
+ {file = "rpds_py-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15"},
+ {file = "rpds_py-0.27.1-cp311-cp311-win_arm64.whl", hash = "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746"},
+ {file = "rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90"},
+ {file = "rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5"},
+ {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e"},
+ {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881"},
+ {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec"},
+ {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb"},
+ {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5"},
+ {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a"},
+ {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444"},
+ {file = "rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a"},
+ {file = "rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1"},
+ {file = "rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998"},
+ {file = "rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39"},
+ {file = "rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594"},
+ {file = "rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502"},
+ {file = "rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b"},
+ {file = "rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf"},
+ {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83"},
+ {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf"},
+ {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2"},
+ {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0"},
+ {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418"},
+ {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d"},
+ {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274"},
+ {file = "rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd"},
+ {file = "rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2"},
+ {file = "rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002"},
+ {file = "rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3"},
+ {file = "rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83"},
+ {file = "rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d"},
+ {file = "rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228"},
+ {file = "rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92"},
+ {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2"},
+ {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723"},
+ {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802"},
+ {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f"},
+ {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2"},
+ {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21"},
+ {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef"},
+ {file = "rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081"},
+ {file = "rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd"},
+ {file = "rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7"},
+ {file = "rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688"},
+ {file = "rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797"},
+ {file = "rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334"},
+ {file = "rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33"},
+ {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a"},
+ {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b"},
+ {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7"},
+ {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136"},
+ {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff"},
+ {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9"},
+ {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60"},
+ {file = "rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e"},
+ {file = "rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212"},
+ {file = "rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675"},
+ {file = "rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3"},
+ {file = "rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456"},
+ {file = "rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3"},
+ {file = "rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2"},
+ {file = "rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4"},
+ {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e"},
+ {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817"},
+ {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec"},
+ {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a"},
+ {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8"},
+ {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48"},
+ {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb"},
+ {file = "rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734"},
+ {file = "rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb"},
+ {file = "rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0"},
+ {file = "rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a"},
+ {file = "rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772"},
+ {file = "rpds_py-0.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c918c65ec2e42c2a78d19f18c553d77319119bf43aa9e2edf7fb78d624355527"},
+ {file = "rpds_py-0.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1fea2b1a922c47c51fd07d656324531adc787e415c8b116530a1d29c0516c62d"},
+ {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbf94c58e8e0cd6b6f38d8de67acae41b3a515c26169366ab58bdca4a6883bb8"},
+ {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2a8fed130ce946d5c585eddc7c8eeef0051f58ac80a8ee43bd17835c144c2cc"},
+ {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:037a2361db72ee98d829bc2c5b7cc55598ae0a5e0ec1823a56ea99374cfd73c1"},
+ {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5281ed1cc1d49882f9997981c88df1a22e140ab41df19071222f7e5fc4e72125"},
+ {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd50659a069c15eef8aa3d64bbef0d69fd27bb4a50c9ab4f17f83a16cbf8905"},
+ {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:c4b676c4ae3921649a15d28ed10025548e9b561ded473aa413af749503c6737e"},
+ {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:079bc583a26db831a985c5257797b2b5d3affb0386e7ff886256762f82113b5e"},
+ {file = "rpds_py-0.27.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e44099bd522cba71a2c6b97f68e19f40e7d85399de899d66cdb67b32d7cb786"},
+ {file = "rpds_py-0.27.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e202e6d4188e53c6661af813b46c37ca2c45e497fc558bacc1a7630ec2695aec"},
+ {file = "rpds_py-0.27.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f41f814b8eaa48768d1bb551591f6ba45f87ac76899453e8ccd41dba1289b04b"},
+ {file = "rpds_py-0.27.1-cp39-cp39-win32.whl", hash = "sha256:9e71f5a087ead99563c11fdaceee83ee982fd39cf67601f4fd66cb386336ee52"},
+ {file = "rpds_py-0.27.1-cp39-cp39-win_amd64.whl", hash = "sha256:71108900c9c3c8590697244b9519017a400d9ba26a36c48381b3f64743a44aab"},
+ {file = "rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf"},
+ {file = "rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3"},
+ {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636"},
+ {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8"},
+ {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc"},
+ {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8"},
+ {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc"},
+ {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71"},
+ {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad"},
+ {file = "rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab"},
+ {file = "rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059"},
+ {file = "rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b"},
+ {file = "rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819"},
+ {file = "rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df"},
+ {file = "rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3"},
+ {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9"},
+ {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc"},
+ {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4"},
+ {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66"},
+ {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e"},
+ {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c"},
+ {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf"},
+ {file = "rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf"},
+ {file = "rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6"},
+ {file = "rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a"},
+ {file = "rpds_py-0.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aa8933159edc50be265ed22b401125c9eebff3171f570258854dbce3ecd55475"},
+ {file = "rpds_py-0.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a50431bf02583e21bf273c71b89d710e7a710ad5e39c725b14e685610555926f"},
+ {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78af06ddc7fe5cc0e967085a9115accee665fb912c22a3f54bad70cc65b05fe6"},
+ {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70d0738ef8fee13c003b100c2fbd667ec4f133468109b3472d249231108283a3"},
+ {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2f6fd8a1cea5bbe599b6e78a6e5ee08db434fc8ffea51ff201c8765679698b3"},
+ {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8177002868d1426305bb5de1e138161c2ec9eb2d939be38291d7c431c4712df8"},
+ {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:008b839781d6c9bf3b6a8984d1d8e56f0ec46dc56df61fd669c49b58ae800400"},
+ {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:a55b9132bb1ade6c734ddd2759c8dc132aa63687d259e725221f106b83a0e485"},
+ {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a46fdec0083a26415f11d5f236b79fa1291c32aaa4a17684d82f7017a1f818b1"},
+ {file = "rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8a63b640a7845f2bdd232eb0d0a4a2dd939bcdd6c57e6bb134526487f3160ec5"},
+ {file = "rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:7e32721e5d4922deaaf963469d795d5bde6093207c52fec719bd22e5d1bedbc4"},
+ {file = "rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2c426b99a068601b5f4623573df7a7c3d72e87533a2dd2253353a03e7502566c"},
+ {file = "rpds_py-0.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4fc9b7fe29478824361ead6e14e4f5aed570d477e06088826537e202d25fe859"},
+ {file = "rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8"},
+]
+
[[package]]
name = "ruff"
version = "0.12.12"
@@ -1927,4 +2251,4 @@ dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"]
[metadata]
lock-version = "2.1"
python-versions = "^3.13"
-content-hash = "01523761e407eb4721a6ac25255a75913ec9eb170901cd8ad47a4dc31857fef4"
+content-hash = "9049da9f95908d67870adae3eb3a34cf3f62f9d7008f82d36958cc7dabfc4ab2"
diff --git a/pyproject.toml b/pyproject.toml
index 4dfd014..7306c4a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -20,6 +20,7 @@ cookiecutter = "^2.6.0"
graphviz = "^0.21"
ipykernel = "^6.30.1"
isort = "^6.0.1"
+jupytext = "^1.16.6"
mypy = "^1.17.1"
nbqa = "^1.9.1"
pre-commit = "^4.3.0"
diff --git a/scripts/shared b/scripts/shared
index d6e9580..0216476 160000
--- a/scripts/shared
+++ b/scripts/shared
@@ -1 +1 @@
-Subproject commit d6e9580673bd0b1b6908add1fc721ad6e3489f7f
+Subproject commit 021647623d9d0304e3e1004e34c766b987f79a25
diff --git a/sonar-project.properties b/sonar-project.properties
index cb6e2c2..bdfd507 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -8,7 +8,7 @@ sonar.projectVersion=1.0
sonar.sources=leetcode,leetcode_py
sonar.tests=leetcode
sonar.test.inclusions=**/tests.py
-sonar.exclusions=**/conftest.py,**/.templates/**,**/__pycache__/**,**/.venv/**
+sonar.exclusions=**/conftest.py,**/.templates/**,**/__pycache__/**,**/.venv/**,**/playground.py
# Python specific settings
sonar.python.version=3.13
From af405d8b258e141b9466a15792af481753045f67 Mon Sep 17 00:00:00 2001
From: Wisaroot <66859294+wisarootl@users.noreply.github.com>
Date: Sat, 13 Sep 2025 11:08:49 +0700
Subject: [PATCH 03/57] feat: add find_all_anagrams_in_a_string (#38)
---
.amazonq/rules/development-rules.md | 14 ++
.amazonq/rules/test-case-enhancement.md | 16 +-
.pre-commit-config.yaml | 2 +-
.templates/leetcode/json/accounts_merge.json | 71 +++++---
...e_from_preorder_and_inorder_traversal.json | 62 +++++++
.../leetcode/json/diagonal_traverse.json | 62 +++++++
.../json/find_all_anagrams_in_a_string.json | 64 +++++++
...letter_combinations_of_a_phone_number.json | 63 +++++++
.templates/leetcode/json/subsets.json | 62 +++++++
.templates/leetcode/json/unique_paths.json | 64 +++++++
.templates/leetcode/json/word_search.json | 67 +++++++
Makefile | 2 +-
leetcode/accounts_merge/README.md | 14 +-
leetcode/accounts_merge/test_solution.py | 71 ++++----
.../README.md | 39 ++++
.../__init__.py | 0
.../helpers.py | 12 ++
.../playground.py | 30 ++++
.../solution.py | 59 ++++++
.../test_solution.py | 41 +++++
leetcode/diagonal_traverse/README.md | 37 ++++
leetcode/diagonal_traverse/__init__.py | 0
leetcode/diagonal_traverse/helpers.py | 8 +
leetcode/diagonal_traverse/playground.py | 29 +++
leetcode/diagonal_traverse/solution.py | 70 ++++++++
leetcode/diagonal_traverse/test_solution.py | 34 ++++
.../find_all_anagrams_in_a_string/README.md | 43 +++++
.../find_all_anagrams_in_a_string/__init__.py | 0
.../find_all_anagrams_in_a_string/helpers.py | 8 +
.../playground.py | 30 ++++
.../find_all_anagrams_in_a_string/solution.py | 56 ++++++
.../test_solution.py | 33 ++++
.../README.md | 43 +++++
.../__init__.py | 0
.../helpers.py | 10 ++
.../playground.py | 29 +++
.../solution.py | 31 ++++
.../test_solution.py | 169 ++++++++++++++++++
leetcode/subsets/README.md | 35 ++++
leetcode/subsets/__init__.py | 0
leetcode/subsets/helpers.py | 11 ++
leetcode/subsets/playground.py | 29 +++
leetcode/subsets/solution.py | 16 ++
leetcode/subsets/test_solution.py | 53 ++++++
leetcode/unique_paths/README.md | 43 +++++
leetcode/unique_paths/__init__.py | 0
leetcode/unique_paths/helpers.py | 8 +
leetcode/unique_paths/playground.py | 30 ++++
leetcode/unique_paths/solution.py | 29 +++
leetcode/unique_paths/test_solution.py | 34 ++++
leetcode/word_search/README.md | 52 ++++++
leetcode/word_search/__init__.py | 0
leetcode/word_search/helpers.py | 8 +
leetcode/word_search/playground.py | 30 ++++
leetcode/word_search/solution.py | 42 +++++
leetcode/word_search/test_solution.py | 44 +++++
pyproject.toml | 3 +
57 files changed, 1840 insertions(+), 72 deletions(-)
create mode 100644 .templates/leetcode/json/construct_binary_tree_from_preorder_and_inorder_traversal.json
create mode 100644 .templates/leetcode/json/diagonal_traverse.json
create mode 100644 .templates/leetcode/json/find_all_anagrams_in_a_string.json
create mode 100644 .templates/leetcode/json/letter_combinations_of_a_phone_number.json
create mode 100644 .templates/leetcode/json/subsets.json
create mode 100644 .templates/leetcode/json/unique_paths.json
create mode 100644 .templates/leetcode/json/word_search.json
create mode 100644 leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/README.md
create mode 100644 leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/__init__.py
create mode 100644 leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/helpers.py
create mode 100644 leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/playground.py
create mode 100644 leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/solution.py
create mode 100644 leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/test_solution.py
create mode 100644 leetcode/diagonal_traverse/README.md
create mode 100644 leetcode/diagonal_traverse/__init__.py
create mode 100644 leetcode/diagonal_traverse/helpers.py
create mode 100644 leetcode/diagonal_traverse/playground.py
create mode 100644 leetcode/diagonal_traverse/solution.py
create mode 100644 leetcode/diagonal_traverse/test_solution.py
create mode 100644 leetcode/find_all_anagrams_in_a_string/README.md
create mode 100644 leetcode/find_all_anagrams_in_a_string/__init__.py
create mode 100644 leetcode/find_all_anagrams_in_a_string/helpers.py
create mode 100644 leetcode/find_all_anagrams_in_a_string/playground.py
create mode 100644 leetcode/find_all_anagrams_in_a_string/solution.py
create mode 100644 leetcode/find_all_anagrams_in_a_string/test_solution.py
create mode 100644 leetcode/letter_combinations_of_a_phone_number/README.md
create mode 100644 leetcode/letter_combinations_of_a_phone_number/__init__.py
create mode 100644 leetcode/letter_combinations_of_a_phone_number/helpers.py
create mode 100644 leetcode/letter_combinations_of_a_phone_number/playground.py
create mode 100644 leetcode/letter_combinations_of_a_phone_number/solution.py
create mode 100644 leetcode/letter_combinations_of_a_phone_number/test_solution.py
create mode 100644 leetcode/subsets/README.md
create mode 100644 leetcode/subsets/__init__.py
create mode 100644 leetcode/subsets/helpers.py
create mode 100644 leetcode/subsets/playground.py
create mode 100644 leetcode/subsets/solution.py
create mode 100644 leetcode/subsets/test_solution.py
create mode 100644 leetcode/unique_paths/README.md
create mode 100644 leetcode/unique_paths/__init__.py
create mode 100644 leetcode/unique_paths/helpers.py
create mode 100644 leetcode/unique_paths/playground.py
create mode 100644 leetcode/unique_paths/solution.py
create mode 100644 leetcode/unique_paths/test_solution.py
create mode 100644 leetcode/word_search/README.md
create mode 100644 leetcode/word_search/__init__.py
create mode 100644 leetcode/word_search/helpers.py
create mode 100644 leetcode/word_search/playground.py
create mode 100644 leetcode/word_search/solution.py
create mode 100644 leetcode/word_search/test_solution.py
diff --git a/.amazonq/rules/development-rules.md b/.amazonq/rules/development-rules.md
index aae1892..d7f1080 100644
--- a/.amazonq/rules/development-rules.md
+++ b/.amazonq/rules/development-rules.md
@@ -18,6 +18,20 @@
- Test all: `make test`
- Beautiful logging with loguru
+### Multiple Solution Classes Pattern
+
+When implementing multiple approaches (e.g., Solution, SolutionMath), use parametrized testing:
+
+```python
+@pytest.mark.parametrize("solution_class", [Solution, SolutionMath])
+@pytest.mark.parametrize("input_params, expected", test_cases)
+def test_method(self, solution_class, input_params, expected):
+ result = run_helper(solution_class, *input_params)
+ assert_helper(result, expected)
+```
+
+This pattern tests all solution approaches with the same test cases, ensuring consistency across implementations.
+
## File Structure
Each problem has:
diff --git a/.amazonq/rules/test-case-enhancement.md b/.amazonq/rules/test-case-enhancement.md
index 11b5019..8b341d7 100644
--- a/.amazonq/rules/test-case-enhancement.md
+++ b/.amazonq/rules/test-case-enhancement.md
@@ -2,7 +2,7 @@
## Simple Enhancement Workflow
-When user requests test case enhancement:
+When user requests test case enhancement or **test reproducibility verification**:
### 1. Problem Resolution
@@ -69,6 +69,19 @@ make p-gen PROBLEM={problem_name} FORCE=1
make p-lint PROBLEM={problem_name}
```
+## Test Reproducibility Verification
+
+Use this same workflow when CI tests fail due to reproducibility issues:
+
+**Process Name**: Test Reproducibility Verification
+
+**When to Use**:
+
+- CI test failures in reproducibility checks
+- Inconsistent test results between environments
+- Missing edge cases causing coverage gaps
+- Need to ensure 100% code coverage
+
## Success Criteria
- All tests pass with enhanced test cases
@@ -76,3 +89,4 @@ make p-lint PROBLEM={problem_name}
- Original solution code preserved
- **Enhanced test cases in final test_solution.py**
- JSON template updated for future regeneration
+- **100% code coverage including edge cases**
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 23a8453..f080465 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -23,7 +23,7 @@ repos:
- repo: local
hooks:
- id: sync-submodules
- name: Sync git submodules
+ name: sync git submodules
entry: bash -c 'output=$(git submodule update --init --recursive --remote 2>&1); [ -z "$output" ]'
language: system
always_run: true
diff --git a/.templates/leetcode/json/accounts_merge.json b/.templates/leetcode/json/accounts_merge.json
index bcbddb6..ec92c0d 100644
--- a/.templates/leetcode/json/accounts_merge.json
+++ b/.templates/leetcode/json/accounts_merge.json
@@ -5,47 +5,60 @@
"problem_title": "Accounts Merge",
"difficulty": "Medium",
"topics": "Array, Hash Table, String, Depth-First Search, Breadth-First Search, Union Find, Sorting",
- "tags": ["grind-75"],
- "readme_description": "Given a list of `accounts` where each element `accounts[i]` is a list of strings, where the first element `accounts[i][0]` is a name, and the rest of the elements are **emails** representing emails of the account.\n\nNow, we would like to merge these accounts. Two accounts definitely belong to the same person if there is some common email to both accounts. Note that even if two accounts have the same name, they may belong to different people as people could have the same name. A person can have any number of accounts initially, but all of their accounts definitely have the same name.\n\nAfter merging the accounts, return the accounts in the following format: the first element of each account is the name, and the rest of the elements are emails **in sorted order**. The accounts themselves can be returned in **any order**.",
- "readme_examples": [
- {
- "content": "```\nInput: accounts = [[\"John\",\"johnsmith@mail.com\",\"john_newyork@mail.com\"],[\"John\",\"johnsmith@mail.com\",\"john00@mail.com\"],[\"Mary\",\"mary@mail.com\"],[\"John\",\"johnnybravo@mail.com\"]]\nOutput: [[\"John\",\"john00@mail.com\",\"john_newyork@mail.com\",\"johnsmith@mail.com\"],[\"Mary\",\"mary@mail.com\"],[\"John\",\"johnnybravo@mail.com\"]]\n```\n**Explanation:** The first and second John's are the same person as they have the common email \"johnsmith@mail.com\". The third John and Mary are different people as none of their email addresses are used by other accounts."
- },
- {
- "content": "```\nInput: accounts = [[\"Gabe\",\"Gabe0@m.co\",\"Gabe3@m.co\",\"Gabe1@m.co\"],[\"Kevin\",\"Kevin3@m.co\",\"Kevin5@m.co\",\"Kevin0@m.co\"],[\"Ethan\",\"Ethan5@m.co\",\"Ethan4@m.co\",\"Ethan0@m.co\"],[\"Hanzo\",\"Hanzo3@m.co\",\"Hanzo1@m.co\",\"Hanzo0@m.co\"],[\"Fern\",\"Fern5@m.co\",\"Fern1@m.co\",\"Fern0@m.co\"]]\nOutput: [[\"Ethan\",\"Ethan0@m.co\",\"Ethan4@m.co\",\"Ethan5@m.co\"],[\"Gabe\",\"Gabe0@m.co\",\"Gabe1@m.co\",\"Gabe3@m.co\"],[\"Hanzo\",\"Hanzo0@m.co\",\"Hanzo1@m.co\",\"Hanzo3@m.co\"],[\"Kevin\",\"Kevin0@m.co\",\"Kevin3@m.co\",\"Kevin5@m.co\"],[\"Fern\",\"Fern0@m.co\",\"Fern1@m.co\",\"Fern5@m.co\"]]\n```"
- }
- ],
- "readme_constraints": "- `1 <= accounts.length <= 1000`\n- `2 <= accounts[i].length <= 10`\n- `1 <= accounts[i][j].length <= 30`\n- `accounts[i][0]` consists of English letters.\n- `accounts[i][j] (for j > 0)` is a valid email.",
+ "_tags": { "list": ["grind-75"] },
+ "readme_description": "Given a list of `accounts` where each element `accounts[i]` is a list of strings, where the first element `accounts[i][0]` is a name, and the rest of the elements are emails representing emails of the account.\n\nNow, we would like to merge these accounts. Two accounts definitely belong to the same person if there is some common email to both accounts. Note that even if two accounts have the same name, they may belong to different people as people could have the same name. A person can have any number of accounts initially, but all of their accounts definitely have the same name.\n\nAfter merging the accounts, return the accounts in the following format: the first element of each account is the name, and the rest of the elements are emails in sorted order. The accounts themselves can be returned in any order.",
+ "_readme_examples": {
+ "list": [
+ {
+ "content": "```\nInput: accounts = [[\"John\",\"johnsmith@mail.com\",\"john_newyork@mail.com\"],[\"John\",\"johnsmith@mail.com\",\"john00@mail.com\"],[\"Mary\",\"mary@mail.com\"],[\"John\",\"johnnybravo@mail.com\"]]\nOutput: [[\"John\",\"john00@mail.com\",\"john_newyork@mail.com\",\"johnsmith@mail.com\"],[\"Mary\",\"mary@mail.com\"],[\"John\",\"johnnybravo@mail.com\"]]\n```\n**Explanation:** The first and second John's are the same person as they have the common email \"johnsmith@mail.com\". The third John and Mary are different people as none of their email addresses are used by other accounts."
+ },
+ {
+ "content": "```\nInput: accounts = [[\"Gabe\",\"Gabe0@m.co\",\"Gabe3@m.co\",\"Gabe1@m.co\"],[\"Kevin\",\"Kevin3@m.co\",\"Kevin5@m.co\",\"Kevin0@m.co\"],[\"Ethan\",\"Ethan5@m.co\",\"Ethan4@m.co\",\"Ethan0@m.co\"],[\"Hanzo\",\"Hanzo3@m.co\",\"Hanzo1@m.co\",\"Hanzo0@m.co\"],[\"Fern\",\"Fern5@m.co\",\"Fern1@m.co\",\"Fern0@m.co\"]]\nOutput: [[\"Ethan\",\"Ethan0@m.co\",\"Ethan4@m.co\",\"Ethan5@m.co\"],[\"Gabe\",\"Gabe0@m.co\",\"Gabe1@m.co\",\"Gabe3@m.co\"],[\"Hanzo\",\"Hanzo0@m.co\",\"Hanzo1@m.co\",\"Hanzo3@m.co\"],[\"Kevin\",\"Kevin0@m.co\",\"Kevin3@m.co\",\"Kevin5@m.co\"],[\"Fern\",\"Fern0@m.co\",\"Fern1@m.co\",\"Fern5@m.co\"]]\n```"
+ }
+ ]
+ },
+ "readme_constraints": "- 1 <= accounts.length <= 1000\n- 2 <= accounts[i].length <= 10\n- 1 <= accounts[i][j].length <= 30\n- accounts[i][0] consists of English letters.\n- accounts[i][j] (for j > 0) is a valid email.",
+ "readme_additional": "",
"helpers_imports": "",
+ "helpers_content": "",
"helpers_run_name": "accounts_merge",
"helpers_run_signature": "(solution_class: type, accounts: list[list[str]])",
"helpers_run_body": " implementation = solution_class()\n return implementation.accounts_merge(accounts)",
"helpers_assert_name": "accounts_merge",
"helpers_assert_signature": "(result: list[list[str]], expected: list[list[str]]) -> bool",
"helpers_assert_body": " # Sort both result and expected for comparison since order doesn't matter\n result_sorted = [sorted(account) for account in sorted(result)]\n expected_sorted = [sorted(account) for account in sorted(expected)]\n assert result_sorted == expected_sorted\n return True",
- "solution_methods": [
- {
- "name": "accounts_merge",
- "signature": "(self, accounts: list[list[str]]) -> list[list[str]]",
- "body": " # TODO: Implement accounts_merge\n return []"
- }
- ],
+ "solution_imports": "",
+ "solution_contents": "",
+ "solution_class_content": "",
"test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_accounts_merge, run_accounts_merge\nfrom .solution import Solution",
"test_content": "",
"test_class_name": "AccountsMerge",
"test_class_content": " def setup_method(self):\n self.solution = Solution()",
- "test_helper_methods": [],
- "test_methods": [
- {
- "name": "test_accounts_merge",
- "signature": "(self, accounts: list[list[str]], expected: list[list[str]])",
- "parametrize": "accounts, expected",
- "test_cases": "[([[\"John\", \"johnsmith@mail.com\", \"john_newyork@mail.com\"], [\"John\", \"johnsmith@mail.com\", \"john00@mail.com\"], [\"Mary\", \"mary@mail.com\"], [\"John\", \"johnnybravo@mail.com\"]], [[\"John\", \"john00@mail.com\", \"john_newyork@mail.com\", \"johnsmith@mail.com\"], [\"Mary\", \"mary@mail.com\"], [\"John\", \"johnnybravo@mail.com\"]]), ([[\"Gabe\", \"Gabe0@m.co\", \"Gabe3@m.co\", \"Gabe1@m.co\"], [\"Kevin\", \"Kevin3@m.co\", \"Kevin5@m.co\", \"Kevin0@m.co\"], [\"Ethan\", \"Ethan5@m.co\", \"Ethan4@m.co\", \"Ethan0@m.co\"], [\"Hanzo\", \"Hanzo3@m.co\", \"Hanzo1@m.co\", \"Hanzo0@m.co\"], [\"Fern\", \"Fern5@m.co\", \"Fern1@m.co\", \"Fern0@m.co\"]], [[\"Ethan\", \"Ethan0@m.co\", \"Ethan4@m.co\", \"Ethan5@m.co\"], [\"Gabe\", \"Gabe0@m.co\", \"Gabe1@m.co\", \"Gabe3@m.co\"], [\"Hanzo\", \"Hanzo0@m.co\", \"Hanzo1@m.co\", \"Hanzo3@m.co\"], [\"Kevin\", \"Kevin0@m.co\", \"Kevin3@m.co\", \"Kevin5@m.co\"], [\"Fern\", \"Fern0@m.co\", \"Fern1@m.co\", \"Fern5@m.co\"]]), ([[\"John\", \"john@mail.com\"]], [[\"John\", \"john@mail.com\"]]), ([[\"John\", \"john1@mail.com\"], [\"John\", \"john2@mail.com\"], [\"John\", \"john3@mail.com\"]], [[\"John\", \"john1@mail.com\"], [\"John\", \"john2@mail.com\"], [\"John\", \"john3@mail.com\"]]), ([[\"John\", \"a@mail.com\", \"b@mail.com\"], [\"John\", \"b@mail.com\", \"c@mail.com\"], [\"John\", \"d@mail.com\"]], [[\"John\", \"a@mail.com\", \"b@mail.com\", \"c@mail.com\"], [\"John\", \"d@mail.com\"]]), ([[\"Alice\", \"alice@mail.com\", \"alice1@mail.com\"], [\"Alice\", \"alice2@mail.com\", \"alice3@mail.com\"], [\"Alice\", \"alice1@mail.com\", \"alice2@mail.com\"]], [[\"Alice\", \"alice1@mail.com\", \"alice2@mail.com\", \"alice3@mail.com\", \"alice@mail.com\"]]), ([[\"Bob\", \"bob@mail.com\"], [\"Bob\", \"bob@mail.com\"]], [[\"Bob\", \"bob@mail.com\"]]), ([[\"David\", \"david1@mail.com\", \"david2@mail.com\"], [\"David\", \"david3@mail.com\"], [\"David\", \"david2@mail.com\", \"david4@mail.com\"]], [[\"David\", \"david1@mail.com\", \"david2@mail.com\", \"david4@mail.com\"], [\"David\", \"david3@mail.com\"]]), ([[\"Alex\", \"alex@mail.com\"], [\"Alex\", \"alex@mail.com\", \"alex2@mail.com\"], [\"Alex\", \"alex3@mail.com\"]], [[\"Alex\", \"alex2@mail.com\", \"alex@mail.com\"], [\"Alex\", \"alex3@mail.com\"]]), ([[\"Tom\", \"tom1@mail.com\"], [\"Tom\", \"tom2@mail.com\"], [\"Tom\", \"tom3@mail.com\"], [\"Tom\", \"tom4@mail.com\"]], [[\"Tom\", \"tom1@mail.com\"], [\"Tom\", \"tom2@mail.com\"], [\"Tom\", \"tom3@mail.com\"], [\"Tom\", \"tom4@mail.com\"]]), ([[\"Sam\", \"sam@mail.com\", \"sam1@mail.com\", \"sam2@mail.com\"]], [[\"Sam\", \"sam@mail.com\", \"sam1@mail.com\", \"sam2@mail.com\"]])]",
- "body": " result = run_accounts_merge(Solution, accounts)\n assert_accounts_merge(result, expected)"
- }
- ],
+ "_solution_methods": {
+ "list": [
+ {
+ "name": "accounts_merge",
+ "signature": "(self, accounts: list[list[str]]) -> list[list[str]]",
+ "body": " # TODO: Implement accounts_merge\n return []"
+ }
+ ]
+ },
+ "_test_helper_methods": {
+ "list": [{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }]
+ },
+ "_test_methods": {
+ "list": [
+ {
+ "name": "test_accounts_merge",
+ "signature": "(self, accounts: list[list[str]], expected: list[list[str]])",
+ "parametrize": "accounts, expected",
+ "test_cases": "[([['John', 'johnsmith@mail.com', 'john_newyork@mail.com'], ['John', 'johnsmith@mail.com', 'john00@mail.com'], ['Mary', 'mary@mail.com'], ['John', 'johnnybravo@mail.com']], [['John', 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'], ['Mary', 'mary@mail.com'], ['John', 'johnnybravo@mail.com']]), ([['Gabe', 'Gabe0@m.co', 'Gabe3@m.co', 'Gabe1@m.co'], ['Kevin', 'Kevin3@m.co', 'Kevin5@m.co', 'Kevin0@m.co'], ['Ethan', 'Ethan5@m.co', 'Ethan4@m.co', 'Ethan0@m.co'], ['Hanzo', 'Hanzo3@m.co', 'Hanzo1@m.co', 'Hanzo0@m.co'], ['Fern', 'Fern5@m.co', 'Fern1@m.co', 'Fern0@m.co']], [['Ethan', 'Ethan0@m.co', 'Ethan4@m.co', 'Ethan5@m.co'], ['Gabe', 'Gabe0@m.co', 'Gabe1@m.co', 'Gabe3@m.co'], ['Hanzo', 'Hanzo0@m.co', 'Hanzo1@m.co', 'Hanzo3@m.co'], ['Kevin', 'Kevin0@m.co', 'Kevin3@m.co', 'Kevin5@m.co'], ['Fern', 'Fern0@m.co', 'Fern1@m.co', 'Fern5@m.co']]), ([['Alice', 'alice@mail.com']], [['Alice', 'alice@mail.com']]), ([['Bob', 'bob1@mail.com'], ['Bob', 'bob2@mail.com']], [['Bob', 'bob1@mail.com'], ['Bob', 'bob2@mail.com']]), ([['Alice', 'alice@mail.com', 'alice2@mail.com'], ['Alice', 'alice2@mail.com', 'alice3@mail.com']], [['Alice', 'alice2@mail.com', 'alice3@mail.com', 'alice@mail.com']]), ([['A', 'a@mail.com', 'b@mail.com'], ['B', 'b@mail.com', 'c@mail.com'], ['C', 'c@mail.com', 'd@mail.com']], [['A', 'a@mail.com', 'b@mail.com', 'c@mail.com', 'd@mail.com']]), ([['David', 'david@mail.com'], ['David', 'david@mail.com']], [['David', 'david@mail.com']]), ([['Alex', 'alex1@mail.com'], ['Bob', 'bob1@mail.com'], ['Charlie', 'charlie1@mail.com']], [['Alex', 'alex1@mail.com'], ['Bob', 'bob1@mail.com'], ['Charlie', 'charlie1@mail.com']]), ([['John', 'john1@mail.com', 'john2@mail.com'], ['John', 'john3@mail.com'], ['Jane', 'jane1@mail.com']], [['John', 'john1@mail.com', 'john2@mail.com'], ['John', 'john3@mail.com'], ['Jane', 'jane1@mail.com']]), ([['User', 'user@mail.com', 'user1@mail.com'], ['User', 'user2@mail.com', 'user@mail.com'], ['User', 'user3@mail.com', 'user1@mail.com']], [['User', 'user1@mail.com', 'user2@mail.com', 'user3@mail.com', 'user@mail.com']]), ([['Test', 'test1@mail.com'], ['Test', 'test2@mail.com'], ['Test', 'test1@mail.com', 'test3@mail.com']], [['Test', 'test2@mail.com'], ['Test', 'test1@mail.com', 'test3@mail.com']]), ([['Name', 'a@mail.com', 'b@mail.com', 'c@mail.com'], ['Name', 'd@mail.com', 'e@mail.com'], ['Name', 'c@mail.com', 'f@mail.com']], [['Name', 'd@mail.com', 'e@mail.com'], ['Name', 'a@mail.com', 'b@mail.com', 'c@mail.com', 'f@mail.com']])]",
+ "body": " result = run_accounts_merge(Solution, accounts)\n assert_accounts_merge(result, expected)"
+ }
+ ]
+ },
"playground_imports": "from helpers import run_accounts_merge, assert_accounts_merge\nfrom solution import Solution",
- "playground_setup": "# Example test case\naccounts = [[\"John\", \"johnsmith@mail.com\", \"john_newyork@mail.com\"], [\"John\", \"johnsmith@mail.com\", \"john00@mail.com\"], [\"Mary\", \"mary@mail.com\"], [\"John\", \"johnnybravo@mail.com\"]]\nexpected = [[\"John\", \"john00@mail.com\", \"john_newyork@mail.com\", \"johnsmith@mail.com\"], [\"Mary\", \"mary@mail.com\"], [\"John\", \"johnnybravo@mail.com\"]]",
+ "playground_setup": "# Example test case\naccounts = [['John', 'johnsmith@mail.com', 'john_newyork@mail.com'], ['John', 'johnsmith@mail.com', 'john00@mail.com'], ['Mary', 'mary@mail.com'], ['John', 'johnnybravo@mail.com']]\nexpected = [['John', 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'], ['Mary', 'mary@mail.com'], ['John', 'johnnybravo@mail.com']]",
"playground_run": "result = run_accounts_merge(Solution, accounts)\nresult",
"playground_assert": "assert_accounts_merge(result, expected)"
}
diff --git a/.templates/leetcode/json/construct_binary_tree_from_preorder_and_inorder_traversal.json b/.templates/leetcode/json/construct_binary_tree_from_preorder_and_inorder_traversal.json
new file mode 100644
index 0000000..e7e7716
--- /dev/null
+++ b/.templates/leetcode/json/construct_binary_tree_from_preorder_and_inorder_traversal.json
@@ -0,0 +1,62 @@
+{
+ "problem_name": "construct_binary_tree_from_preorder_and_inorder_traversal",
+ "solution_class_name": "Solution",
+ "problem_number": "105",
+ "problem_title": "Construct Binary Tree from Preorder and Inorder Traversal",
+ "difficulty": "Medium",
+ "topics": "Array, Hash Table, Divide and Conquer, Tree, Binary Tree",
+ "_tags": { "list": ["grind-75"] },
+ "readme_description": "Given two integer arrays `preorder` and `inorder` where `preorder` is the preorder traversal of a binary tree and `inorder` is the inorder traversal of the same tree, construct and return the binary tree.",
+ "_readme_examples": {
+ "list": [
+ {
+ "content": "\n\n```\nInput: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]\nOutput: [3,9,20,null,null,15,7]\n```"
+ },
+ { "content": "```\nInput: preorder = [-1], inorder = [-1]\nOutput: [-1]\n```" }
+ ]
+ },
+ "readme_constraints": "- 1 <= preorder.length <= 3000\n- inorder.length == preorder.length\n- -3000 <= preorder[i], inorder[i] <= 3000\n- preorder and inorder consist of unique values.\n- Each value of inorder also appears in preorder.\n- preorder is guaranteed to be the preorder traversal of the tree.\n- inorder is guaranteed to be the inorder traversal of the tree.",
+ "readme_additional": "",
+ "helpers_imports": "from leetcode_py import TreeNode",
+ "helpers_content": "",
+ "helpers_run_name": "build_tree",
+ "helpers_run_signature": "(solution_class: type, preorder: list[int], inorder: list[int])",
+ "helpers_run_body": " implementation = solution_class()\n return implementation.build_tree(preorder, inorder)",
+ "helpers_assert_name": "build_tree",
+ "helpers_assert_signature": "(result: TreeNode | None, expected_list: list[int | None]) -> bool",
+ "helpers_assert_body": " expected = TreeNode.from_list(expected_list) if expected_list else None\n assert result == expected\n return True",
+ "solution_imports": "from leetcode_py import TreeNode",
+ "solution_contents": "",
+ "solution_class_content": "",
+ "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_build_tree, run_build_tree\nfrom .solution import Solution",
+ "test_content": "",
+ "test_class_name": "ConstructBinaryTreeFromPreorderAndInorderTraversal",
+ "test_class_content": " def setup_method(self):\n self.solution = Solution()",
+ "_solution_methods": {
+ "list": [
+ {
+ "name": "build_tree",
+ "signature": "(self, preorder: list[int], inorder: list[int]) -> TreeNode | None",
+ "body": " # TODO: Implement build_tree\n return None"
+ }
+ ]
+ },
+ "_test_helper_methods": {
+ "list": [{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }]
+ },
+ "_test_methods": {
+ "list": [
+ {
+ "name": "test_build_tree",
+ "signature": "(self, preorder: list[int], inorder: list[int], expected_list: list[int | None])",
+ "parametrize": "preorder, inorder, expected_list",
+ "test_cases": "[([], [], []), ([1], [1], [1]), ([3, 9, 20, 15, 7], [9, 3, 15, 20, 7], [3, 9, 20, None, None, 15, 7]), ([-1], [-1], [-1]), ([1, 2], [2, 1], [1, 2]), ([1, 2], [1, 2], [1, None, 2]), ([1, 2, 3], [2, 1, 3], [1, 2, 3]), ([1, 2, 4, 5, 3, 6], [4, 2, 5, 1, 6, 3], [1, 2, 3, 4, 5, 6]), ([1, 2, 3, 4], [1, 2, 3, 4], [1, None, 2, None, 3, None, 4]), ([4, 3, 2, 1], [1, 2, 3, 4], [4, 3, None, 2, None, 1]), ([10, 5, 1, 7, 40, 50], [1, 5, 7, 10, 40, 50], [10, 5, 40, 1, 7, None, 50]), ([1, 3, 2], [1, 2, 3], [1, None, 3, 2]), ([2, 1, 3], [1, 2, 3], [2, 1, 3]), ([5, 3, 2, 1, 4, 6, 7], [1, 2, 3, 4, 5, 6, 7], [5, 3, 6, 2, 4, None, 7, 1]), ([7, 3, 2, 1, 5, 4, 6, 10, 9, 11], [1, 2, 3, 4, 5, 6, 7, 9, 10, 11], [7, 3, 10, 2, 5, 9, 11, 1, None, 4, 6]), ([-3000, -2999, -2998], [-2998, -2999, -3000], [-3000, -2999, None, -2998])]",
+ "body": " result = run_build_tree(Solution, preorder, inorder)\n assert_build_tree(result, expected_list)"
+ }
+ ]
+ },
+ "playground_imports": "from helpers import run_build_tree, assert_build_tree\nfrom solution import Solution\nfrom leetcode_py import TreeNode",
+ "playground_setup": "# Example test case\npreorder = [3, 9, 20, 15, 7]\ninorder = [9, 3, 15, 20, 7]\nexpected_list = [3, 9, 20, None, None, 15, 7]",
+ "playground_run": "result = run_build_tree(Solution, preorder, inorder)\nresult",
+ "playground_assert": "assert_build_tree(result, expected_list)"
+}
diff --git a/.templates/leetcode/json/diagonal_traverse.json b/.templates/leetcode/json/diagonal_traverse.json
new file mode 100644
index 0000000..df17575
--- /dev/null
+++ b/.templates/leetcode/json/diagonal_traverse.json
@@ -0,0 +1,62 @@
+{
+ "problem_name": "diagonal_traverse",
+ "solution_class_name": "Solution",
+ "problem_number": "498",
+ "problem_title": "Diagonal Traverse",
+ "difficulty": "Medium",
+ "topics": "Array, Matrix, Simulation",
+ "_tags": { "list": [] },
+ "readme_description": "Given an `m x n` matrix `mat`, return *an array of all the elements of the array in a diagonal order*.",
+ "_readme_examples": {
+ "list": [
+ {
+ "content": "\n\n```\nInput: mat = [[1,2,3],[4,5,6],[7,8,9]]\nOutput: [1,2,4,7,5,3,6,8,9]\n```"
+ },
+ { "content": "```\nInput: mat = [[1,2],[3,4]]\nOutput: [1,2,3,4]\n```" }
+ ]
+ },
+ "readme_constraints": "- `m == mat.length`\n- `n == mat[i].length`\n- `1 <= m, n <= 10^4`\n- `1 <= m * n <= 10^4`\n- `-10^5 <= mat[i][j] <= 10^5`",
+ "readme_additional": "",
+ "helpers_imports": "",
+ "helpers_content": "",
+ "helpers_run_name": "find_diagonal_order",
+ "helpers_run_signature": "(solution_class: type, mat: list[list[int]])",
+ "helpers_run_body": " implementation = solution_class()\n return implementation.find_diagonal_order(mat)",
+ "helpers_assert_name": "find_diagonal_order",
+ "helpers_assert_signature": "(result: list[int], expected: list[int]) -> bool",
+ "helpers_assert_body": " assert result == expected\n return True",
+ "solution_imports": "",
+ "solution_contents": "",
+ "solution_class_content": "",
+ "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_find_diagonal_order, run_find_diagonal_order\nfrom .solution import Solution",
+ "test_content": "",
+ "test_class_name": "DiagonalTraverse",
+ "test_class_content": " def setup_method(self):\n self.solution = Solution()",
+ "_solution_methods": {
+ "list": [
+ {
+ "name": "find_diagonal_order",
+ "signature": "(self, mat: list[list[int]]) -> list[int]",
+ "body": " # TODO: Implement find_diagonal_order\n return []"
+ }
+ ]
+ },
+ "_test_helper_methods": {
+ "list": [{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }]
+ },
+ "_test_methods": {
+ "list": [
+ {
+ "name": "test_find_diagonal_order",
+ "signature": "(self, mat: list[list[int]], expected: list[int])",
+ "parametrize": "mat, expected",
+ "test_cases": "[([[1, 2, 3], [4, 5, 6], [7, 8, 9]], [1, 2, 4, 7, 5, 3, 6, 8, 9]), ([[1, 2], [3, 4]], [1, 2, 3, 4]), ([[1]], [1]), ([[1, 2, 3]], [1, 2, 3]), ([[1], [2], [3]], [1, 2, 3]), ([[1, 2, 3, 4], [5, 6, 7, 8]], [1, 2, 5, 6, 3, 4, 7, 8]), ([[1, 2], [3, 4], [5, 6]], [1, 2, 3, 5, 4, 6]), ([[1, 2, 3, 4, 5]], [1, 2, 3, 4, 5]), ([[1], [2], [3], [4], [5]], [1, 2, 3, 4, 5]), ([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]], [1, 2, 5, 9, 6, 3, 4, 7, 10, 11, 8, 12]), ([[-1, 0, 1], [2, -3, 4]], [-1, 0, 2, -3, 1, 4]), ([[100]], [100])]",
+ "body": " result = run_find_diagonal_order(Solution, mat)\n assert_find_diagonal_order(result, expected)"
+ }
+ ]
+ },
+ "playground_imports": "from helpers import run_find_diagonal_order, assert_find_diagonal_order\nfrom solution import Solution",
+ "playground_setup": "# Example test case\nmat = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]\nexpected = [1, 2, 4, 7, 5, 3, 6, 8, 9]",
+ "playground_run": "result = run_find_diagonal_order(Solution, mat)\nresult",
+ "playground_assert": "assert_find_diagonal_order(result, expected)"
+}
diff --git a/.templates/leetcode/json/find_all_anagrams_in_a_string.json b/.templates/leetcode/json/find_all_anagrams_in_a_string.json
new file mode 100644
index 0000000..fa9cd1a
--- /dev/null
+++ b/.templates/leetcode/json/find_all_anagrams_in_a_string.json
@@ -0,0 +1,64 @@
+{
+ "problem_name": "find_all_anagrams_in_a_string",
+ "solution_class_name": "Solution",
+ "problem_number": "438",
+ "problem_title": "Find All Anagrams in a String",
+ "difficulty": "Medium",
+ "topics": "Hash Table, String, Sliding Window",
+ "_tags": { "list": ["grind-75"] },
+ "readme_description": "Given two strings `s` and `p`, return an array of all the start indices of `p`'s anagrams in `s`. You may return the answer in any order.\n\nAn **anagram** is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.",
+ "_readme_examples": {
+ "list": [
+ {
+ "content": "```\nInput: s = \"cbaebabacd\", p = \"abc\"\nOutput: [0,6]\n```\n**Explanation:**\nThe substring with start index = 0 is \"cba\", which is an anagram of \"abc\".\nThe substring with start index = 6 is \"bac\", which is an anagram of \"abc\"."
+ },
+ {
+ "content": "```\nInput: s = \"abab\", p = \"ab\"\nOutput: [0,1,2]\n```\n**Explanation:**\nThe substring with start index = 0 is \"ab\", which is an anagram of \"ab\".\nThe substring with start index = 1 is \"ba\", which is an anagram of \"ab\".\nThe substring with start index = 2 is \"ab\", which is an anagram of \"ab\"."
+ }
+ ]
+ },
+ "readme_constraints": "- 1 <= s.length, p.length <= 3 * 10^4\n- s and p consist of lowercase English letters.",
+ "readme_additional": "",
+ "helpers_imports": "",
+ "helpers_content": "",
+ "helpers_run_name": "find_anagrams",
+ "helpers_run_signature": "(solution_class: type, s: str, p: str)",
+ "helpers_run_body": " implementation = solution_class()\n return implementation.find_anagrams(s, p)",
+ "helpers_assert_name": "find_anagrams",
+ "helpers_assert_signature": "(result: list[int], expected: list[int]) -> bool",
+ "helpers_assert_body": " assert sorted(result) == sorted(expected)\n return True",
+ "solution_imports": "",
+ "solution_contents": "",
+ "solution_class_content": "",
+ "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_find_anagrams, run_find_anagrams\nfrom .solution import Solution",
+ "test_content": "",
+ "test_class_name": "FindAllAnagramsInAString",
+ "test_class_content": " def setup_method(self):\n self.solution = Solution()",
+ "_solution_methods": {
+ "list": [
+ {
+ "name": "find_anagrams",
+ "signature": "(self, s: str, p: str) -> list[int]",
+ "body": " # TODO: Implement find_anagrams\n return []"
+ }
+ ]
+ },
+ "_test_helper_methods": {
+ "list": [{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }]
+ },
+ "_test_methods": {
+ "list": [
+ {
+ "name": "test_find_anagrams",
+ "signature": "(self, s: str, p: str, expected: list[int])",
+ "parametrize": "s, p, expected",
+ "test_cases": "[('cbaebabacd', 'abc', [0, 6]), ('abab', 'ab', [0, 1, 2]), ('a', 'aa', []), ('aa', 'aa', [0]), ('abcdefg', 'xyz', []), ('aab', 'ab', [1]), ('aaab', 'ab', [2]), ('baa', 'aa', [1]), ('abacabad', 'aaab', []), ('ababacb', 'abc', [3, 4]), ('abaacbabc', 'abc', [3, 4, 6]), ('abab', 'abab', [0])]",
+ "body": " result = run_find_anagrams(Solution, s, p)\n assert_find_anagrams(result, expected)"
+ }
+ ]
+ },
+ "playground_imports": "from helpers import run_find_anagrams, assert_find_anagrams\nfrom solution import Solution",
+ "playground_setup": "# Example test case\ns = 'cbaebabacd'\np = 'abc'\nexpected = [0, 6]",
+ "playground_run": "result = run_find_anagrams(Solution, s, p)\nresult",
+ "playground_assert": "assert_find_anagrams(result, expected)"
+}
diff --git a/.templates/leetcode/json/letter_combinations_of_a_phone_number.json b/.templates/leetcode/json/letter_combinations_of_a_phone_number.json
new file mode 100644
index 0000000..990afaf
--- /dev/null
+++ b/.templates/leetcode/json/letter_combinations_of_a_phone_number.json
@@ -0,0 +1,63 @@
+{
+ "problem_name": "letter_combinations_of_a_phone_number",
+ "solution_class_name": "Solution",
+ "problem_number": "17",
+ "problem_title": "Letter Combinations of a Phone Number",
+ "difficulty": "Medium",
+ "topics": "Hash Table, String, Backtracking",
+ "_tags": { "list": ["grind-75"] },
+ "readme_description": "Given a string containing digits from `2-9` inclusive, return all possible letter combinations that the number could represent. Return the answer in **any order**.\n\nA mapping of digits to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.",
+ "_readme_examples": {
+ "list": [
+ {
+ "content": "\n\n```\nInput: digits = \"23\"\nOutput: [\"ad\",\"ae\",\"af\",\"bd\",\"be\",\"bf\",\"cd\",\"ce\",\"cf\"]\n```"
+ },
+ { "content": "```\nInput: digits = \"\"\nOutput: []\n```" },
+ { "content": "```\nInput: digits = \"2\"\nOutput: [\"a\",\"b\",\"c\"]\n```" }
+ ]
+ },
+ "readme_constraints": "- `0 <= digits.length <= 4`\n- `digits[i]` is a digit in the range `['2', '9']`.",
+ "readme_additional": "",
+ "helpers_imports": "",
+ "helpers_content": "",
+ "helpers_run_name": "letter_combinations",
+ "helpers_run_signature": "(solution_class: type, digits: str)",
+ "helpers_run_body": " implementation = solution_class()\n return implementation.letter_combinations(digits)",
+ "helpers_assert_name": "letter_combinations",
+ "helpers_assert_signature": "(result: list[str], expected: list[str]) -> bool",
+ "helpers_assert_body": " result.sort()\n expected.sort()\n assert result == expected\n return True",
+ "solution_imports": "",
+ "solution_contents": "",
+ "solution_class_content": "",
+ "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_letter_combinations, run_letter_combinations\nfrom .solution import Solution",
+ "test_content": "",
+ "test_class_name": "LetterCombinationsOfAPhoneNumber",
+ "test_class_content": " def setup_method(self):\n self.solution = Solution()",
+ "_solution_methods": {
+ "list": [
+ {
+ "name": "letter_combinations",
+ "signature": "(self, digits: str) -> list[str]",
+ "body": " # TODO: Implement letter_combinations\n return []"
+ }
+ ]
+ },
+ "_test_helper_methods": {
+ "list": [{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }]
+ },
+ "_test_methods": {
+ "list": [
+ {
+ "name": "test_letter_combinations",
+ "signature": "(self, digits: str, expected: list[str])",
+ "parametrize": "digits, expected",
+ "test_cases": "[('23', ['ad', 'ae', 'af', 'bd', 'be', 'bf', 'cd', 'ce', 'cf']), ('', []), ('2', ['a', 'b', 'c']), ('234', ['adg', 'adh', 'adi', 'aeg', 'aeh', 'aei', 'afg', 'afh', 'afi', 'bdg', 'bdh', 'bdi', 'beg', 'beh', 'bei', 'bfg', 'bfh', 'bfi', 'cdg', 'cdh', 'cdi', 'ceg', 'ceh', 'cei', 'cfg', 'cfh', 'cfi']), ('7', ['p', 'q', 'r', 's']), ('9', ['w', 'x', 'y', 'z']), ('79', ['pw', 'px', 'py', 'pz', 'qw', 'qx', 'qy', 'qz', 'rw', 'rx', 'ry', 'rz', 'sw', 'sx', 'sy', 'sz']), ('22', ['aa', 'ab', 'ac', 'ba', 'bb', 'bc', 'ca', 'cb', 'cc']), ('3456', ['dgjm', 'dgjn', 'dgjo', 'dgkm', 'dgkn', 'dgko', 'dglm', 'dgln', 'dglo', 'dhjm', 'dhjn', 'dhjo', 'dhkm', 'dhkn', 'dhko', 'dhlm', 'dhln', 'dhlo', 'dijm', 'dijn', 'dijo', 'dikm', 'dikn', 'diko', 'dilm', 'diln', 'dilo', 'egjm', 'egjn', 'egjo', 'egkm', 'egkn', 'egko', 'eglm', 'egln', 'eglo', 'ehjm', 'ehjn', 'ehjo', 'ehkm', 'ehkn', 'ehko', 'ehlm', 'ehln', 'ehlo', 'eijm', 'eijn', 'eijo', 'eikm', 'eikn', 'eiko', 'eilm', 'eiln', 'eilo', 'fgjm', 'fgjn', 'fgjo', 'fgkm', 'fgkn', 'fgko', 'fglm', 'fgln', 'fglo', 'fhjm', 'fhjn', 'fhjo', 'fhkm', 'fhkn', 'fhko', 'fhlm', 'fhln', 'fhlo', 'fijm', 'fijn', 'fijo', 'fikm', 'fikn', 'fiko', 'film', 'filn', 'filo']), ('25', ['aj', 'ak', 'al', 'bj', 'bk', 'bl', 'cj', 'ck', 'cl']), ('78', ['pt', 'pu', 'pv', 'qt', 'qu', 'qv', 'rt', 'ru', 'rv', 'st', 'su', 'sv']), ('89', ['tw', 'tx', 'ty', 'tz', 'uw', 'ux', 'uy', 'uz', 'vw', 'vx', 'vy', 'vz'])]",
+ "body": " result = run_letter_combinations(Solution, digits)\n assert_letter_combinations(result, expected)"
+ }
+ ]
+ },
+ "playground_imports": "from helpers import run_letter_combinations, assert_letter_combinations\nfrom solution import Solution",
+ "playground_setup": "# Example test case\ndigits = '23'\nexpected = ['ad', 'ae', 'af', 'bd', 'be', 'bf', 'cd', 'ce', 'cf']",
+ "playground_run": "result = run_letter_combinations(Solution, digits)\nresult",
+ "playground_assert": "assert_letter_combinations(result, expected)"
+}
diff --git a/.templates/leetcode/json/subsets.json b/.templates/leetcode/json/subsets.json
new file mode 100644
index 0000000..a30f10a
--- /dev/null
+++ b/.templates/leetcode/json/subsets.json
@@ -0,0 +1,62 @@
+{
+ "problem_name": "subsets",
+ "solution_class_name": "Solution",
+ "problem_number": "78",
+ "problem_title": "Subsets",
+ "difficulty": "Medium",
+ "topics": "Array, Backtracking, Bit Manipulation",
+ "_tags": { "list": ["grind-75"] },
+ "readme_description": "Given an integer array `nums` of **unique** elements, return *all possible* subsets (the power set).\n\nThe solution set **must not** contain duplicate subsets. Return the solution in **any order**.",
+ "_readme_examples": {
+ "list": [
+ {
+ "content": "```\nInput: nums = [1,2,3]\nOutput: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]\n```"
+ },
+ { "content": "```\nInput: nums = [0]\nOutput: [[],[0]]\n```" }
+ ]
+ },
+ "readme_constraints": "- 1 <= nums.length <= 10\n- -10 <= nums[i] <= 10\n- All the numbers of nums are unique.",
+ "readme_additional": "",
+ "helpers_imports": "",
+ "helpers_content": "",
+ "helpers_run_name": "subsets",
+ "helpers_run_signature": "(solution_class: type, nums: list[int])",
+ "helpers_run_body": " implementation = solution_class()\n return implementation.subsets(nums)",
+ "helpers_assert_name": "subsets",
+ "helpers_assert_signature": "(result: list[list[int]], expected: list[list[int]]) -> bool",
+ "helpers_assert_body": " # Sort both result and expected for comparison since order doesn't matter\n result_sorted = [sorted(subset) for subset in sorted(result)]\n expected_sorted = [sorted(subset) for subset in sorted(expected)]\n assert result_sorted == expected_sorted\n return True",
+ "solution_imports": "",
+ "solution_contents": "",
+ "solution_class_content": "",
+ "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_subsets, run_subsets\nfrom .solution import Solution",
+ "test_content": "",
+ "test_class_name": "Subsets",
+ "test_class_content": " def setup_method(self):\n self.solution = Solution()",
+ "_solution_methods": {
+ "list": [
+ {
+ "name": "subsets",
+ "signature": "(self, nums: list[int]) -> list[list[int]]",
+ "body": " # TODO: Implement subsets\n return []"
+ }
+ ]
+ },
+ "_test_helper_methods": {
+ "list": [{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }]
+ },
+ "_test_methods": {
+ "list": [
+ {
+ "name": "test_subsets",
+ "signature": "(self, nums: list[int], expected: list[list[int]])",
+ "parametrize": "nums, expected",
+ "test_cases": "[([1, 2, 3], [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]), ([0], [[], [0]]), ([1], [[], [1]]), ([1, 2], [[], [1], [2], [1, 2]]), ([4, 1, 0], [[], [4], [4, 1], [4, 1, 0], [4, 0], [1], [1, 0], [0]]), ([-1, 0, 1], [[], [-1], [-1, 0], [-1, 0, 1], [-1, 1], [0], [0, 1], [1]]), ([5], [[], [5]]), ([2, 1, 3], [[], [2], [2, 1], [2, 1, 3], [2, 3], [1], [1, 3], [3]]), ([10], [[], [10]]), ([-10, 10], [[], [-10], [-10, 10], [10]]), ([1, 2, 3, 4], [[], [1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 4], [1, 3], [1, 3, 4], [1, 4], [2], [2, 3], [2, 3, 4], [2, 4], [3], [3, 4], [4]]), ([5, 2], [[], [5], [5, 2], [2]])]",
+ "body": " result = run_subsets(Solution, nums)\n assert_subsets(result, expected)"
+ }
+ ]
+ },
+ "playground_imports": "from helpers import run_subsets, assert_subsets\nfrom solution import Solution",
+ "playground_setup": "# Example test case\nnums = [1, 2, 3]\nexpected = [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]",
+ "playground_run": "result = run_subsets(Solution, nums)\nresult",
+ "playground_assert": "assert_subsets(result, expected)"
+}
diff --git a/.templates/leetcode/json/unique_paths.json b/.templates/leetcode/json/unique_paths.json
new file mode 100644
index 0000000..001900d
--- /dev/null
+++ b/.templates/leetcode/json/unique_paths.json
@@ -0,0 +1,64 @@
+{
+ "problem_name": "unique_paths",
+ "solution_class_name": "Solution",
+ "problem_number": "62",
+ "problem_title": "Unique Paths",
+ "difficulty": "Medium",
+ "topics": "Math, Dynamic Programming, Combinatorics",
+ "_tags": { "list": ["grind-75"] },
+ "readme_description": "There is a robot on an `m x n` grid. The robot is initially located at the **top-left corner** (i.e., `grid[0][0]`). The robot tries to move to the **bottom-right corner** (i.e., `grid[m - 1][n - 1]`). The robot can only move either down or right at any point in time.\n\nGiven the two integers `m` and `n`, return *the number of possible unique paths that the robot can take to reach the bottom-right corner*.\n\nThe test cases are generated so that the answer will be less than or equal to `2 * 10^9`.",
+ "_readme_examples": {
+ "list": [
+ {
+ "content": "\n\n```\nInput: m = 3, n = 7\nOutput: 28\n```"
+ },
+ {
+ "content": "```\nInput: m = 3, n = 2\nOutput: 3\n```\n**Explanation:** From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:\n1. Right -> Down -> Down\n2. Down -> Down -> Right\n3. Down -> Right -> Down"
+ }
+ ]
+ },
+ "readme_constraints": "- 1 <= m, n <= 100",
+ "readme_additional": "",
+ "helpers_imports": "",
+ "helpers_content": "",
+ "helpers_run_name": "unique_paths",
+ "helpers_run_signature": "(solution_class: type, m: int, n: int)",
+ "helpers_run_body": " implementation = solution_class()\n return implementation.unique_paths(m, n)",
+ "helpers_assert_name": "unique_paths",
+ "helpers_assert_signature": "(result: int, expected: int) -> bool",
+ "helpers_assert_body": " assert result == expected\n return True",
+ "solution_imports": "",
+ "solution_contents": "",
+ "solution_class_content": "",
+ "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_unique_paths, run_unique_paths\nfrom .solution import Solution",
+ "test_content": "",
+ "test_class_name": "UniquePaths",
+ "test_class_content": " def setup_method(self):\n self.solution = Solution()",
+ "_solution_methods": {
+ "list": [
+ {
+ "name": "unique_paths",
+ "signature": "(self, m: int, n: int) -> int",
+ "body": " # TODO: Implement unique_paths\n return 0"
+ }
+ ]
+ },
+ "_test_helper_methods": {
+ "list": [{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }]
+ },
+ "_test_methods": {
+ "list": [
+ {
+ "name": "test_unique_paths",
+ "signature": "(self, m: int, n: int, expected: int)",
+ "parametrize": "m, n, expected",
+ "test_cases": "[(3, 7, 28), (3, 2, 3), (1, 1, 1), (1, 10, 1), (10, 1, 1), (2, 2, 2), (3, 3, 6), (4, 4, 20), (5, 5, 70), (2, 3, 3), (3, 4, 10), (4, 5, 35), (6, 3, 21), (7, 3, 28), (10, 10, 48620)]",
+ "body": " result = run_unique_paths(Solution, m, n)\n assert_unique_paths(result, expected)"
+ }
+ ]
+ },
+ "playground_imports": "from helpers import run_unique_paths, assert_unique_paths\nfrom solution import Solution",
+ "playground_setup": "# Example test case\nm = 3\nn = 7\nexpected = 28",
+ "playground_run": "result = run_unique_paths(Solution, m, n)\nresult",
+ "playground_assert": "assert_unique_paths(result, expected)"
+}
diff --git a/.templates/leetcode/json/word_search.json b/.templates/leetcode/json/word_search.json
new file mode 100644
index 0000000..776a490
--- /dev/null
+++ b/.templates/leetcode/json/word_search.json
@@ -0,0 +1,67 @@
+{
+ "problem_name": "word_search",
+ "solution_class_name": "Solution",
+ "problem_number": "79",
+ "problem_title": "Word Search",
+ "difficulty": "Medium",
+ "topics": "Array, String, Backtracking, Depth-First Search, Matrix",
+ "_tags": { "list": ["grind-75"] },
+ "readme_description": "Given an `m x n` grid of characters `board` and a string `word`, return `true` *if* `word` *exists in the grid*.\n\nThe word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than once.",
+ "_readme_examples": {
+ "list": [
+ {
+ "content": "\n\n```\nInput: board = [[\"A\",\"B\",\"C\",\"E\"],[\"S\",\"F\",\"C\",\"S\"],[\"A\",\"D\",\"E\",\"E\"]], word = \"ABCCED\"\nOutput: true\n```"
+ },
+ {
+ "content": "\n\n```\nInput: board = [[\"A\",\"B\",\"C\",\"E\"],[\"S\",\"F\",\"C\",\"S\"],[\"A\",\"D\",\"E\",\"E\"]], word = \"SEE\"\nOutput: true\n```"
+ },
+ {
+ "content": "\n\n```\nInput: board = [[\"A\",\"B\",\"C\",\"E\"],[\"S\",\"F\",\"C\",\"S\"],[\"A\",\"D\",\"E\",\"E\"]], word = \"ABCB\"\nOutput: false\n```"
+ }
+ ]
+ },
+ "readme_constraints": "- `m == board.length`\n- `n = board[i].length`\n- `1 <= m, n <= 6`\n- `1 <= word.length <= 15`\n- `board` and `word` consists of only lowercase and uppercase English letters.",
+ "readme_additional": "**Follow up:** Could you use search pruning to make your solution faster with a larger `board`?",
+ "helpers_imports": "",
+ "helpers_content": "",
+ "helpers_run_name": "exist",
+ "helpers_run_signature": "(solution_class: type, board: list[list[str]], word: str)",
+ "helpers_run_body": " implementation = solution_class()\n return implementation.exist(board, word)",
+ "helpers_assert_name": "exist",
+ "helpers_assert_signature": "(result: bool, expected: bool) -> bool",
+ "helpers_assert_body": " assert result == expected\n return True",
+ "solution_imports": "",
+ "solution_contents": "",
+ "solution_class_content": "",
+ "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .helpers import assert_exist, run_exist\nfrom .solution import Solution",
+ "test_content": "",
+ "test_class_name": "WordSearch",
+ "test_class_content": " def setup_method(self):\n self.solution = Solution()",
+ "_solution_methods": {
+ "list": [
+ {
+ "name": "exist",
+ "signature": "(self, board: list[list[str]], word: str) -> bool",
+ "body": " # TODO: Implement exist\n return False"
+ }
+ ]
+ },
+ "_test_helper_methods": {
+ "list": [{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }]
+ },
+ "_test_methods": {
+ "list": [
+ {
+ "name": "test_exist",
+ "signature": "(self, board: list[list[str]], word: str, expected: bool)",
+ "parametrize": "board, word, expected",
+ "test_cases": "[([['A', 'B', 'C', 'E'], ['S', 'F', 'C', 'S'], ['A', 'D', 'E', 'E']], 'ABCCED', True), ([['A', 'B', 'C', 'E'], ['S', 'F', 'C', 'S'], ['A', 'D', 'E', 'E']], 'SEE', True), ([['A', 'B', 'C', 'E'], ['S', 'F', 'C', 'S'], ['A', 'D', 'E', 'E']], 'ABCB', False), ([['A']], 'A', True), ([['A']], 'B', False), ([['A', 'B'], ['C', 'D']], 'ACDB', True), ([['A', 'B'], ['C', 'D']], 'ABDC', True), ([['A', 'B'], ['C', 'D']], 'ABCD', False), ([['C', 'A', 'A'], ['A', 'A', 'A'], ['B', 'C', 'D']], 'AAB', True), ([['A', 'B', 'C', 'E'], ['S', 'F', 'C', 'S'], ['A', 'D', 'E', 'E']], 'ABCESEEEFS', False), ([['A', 'A', 'A', 'A', 'A', 'A'], ['A', 'A', 'A', 'A', 'A', 'A'], ['A', 'A', 'A', 'A', 'A', 'A'], ['A', 'A', 'A', 'A', 'A', 'A'], ['A', 'A', 'A', 'A', 'A', 'A'], ['A', 'A', 'A', 'A', 'A', 'A']], 'AAAAAAAAAAAAAAB', False), ([['A', 'B', 'C', 'E'], ['S', 'F', 'C', 'S'], ['A', 'D', 'E', 'E']], 'SFCS', True)]",
+ "body": " result = run_exist(Solution, board, word)\n assert_exist(result, expected)"
+ }
+ ]
+ },
+ "playground_imports": "from helpers import run_exist, assert_exist\nfrom solution import Solution",
+ "playground_setup": "# Example test case\nboard = [['A', 'B', 'C', 'E'], ['S', 'F', 'C', 'S'], ['A', 'D', 'E', 'E']]\nword = 'ABCCED'\nexpected = True",
+ "playground_run": "result = run_exist(Solution, board, word)\nresult",
+ "playground_assert": "assert_exist(result, expected)"
+}
diff --git a/Makefile b/Makefile
index 377ff6d..c6eeb6a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
PYTHON_VERSION = 3.13
-PROBLEM ?= two_sum
+PROBLEM ?= find_all_anagrams_in_a_string
FORCE ?= 0
COMMA := ,
diff --git a/leetcode/accounts_merge/README.md b/leetcode/accounts_merge/README.md
index 53a64a3..dd3f61e 100644
--- a/leetcode/accounts_merge/README.md
+++ b/leetcode/accounts_merge/README.md
@@ -8,11 +8,11 @@
## Problem Description
-Given a list of `accounts` where each element `accounts[i]` is a list of strings, where the first element `accounts[i][0]` is a name, and the rest of the elements are **emails** representing emails of the account.
+Given a list of `accounts` where each element `accounts[i]` is a list of strings, where the first element `accounts[i][0]` is a name, and the rest of the elements are emails representing emails of the account.
Now, we would like to merge these accounts. Two accounts definitely belong to the same person if there is some common email to both accounts. Note that even if two accounts have the same name, they may belong to different people as people could have the same name. A person can have any number of accounts initially, but all of their accounts definitely have the same name.
-After merging the accounts, return the accounts in the following format: the first element of each account is the name, and the rest of the elements are emails **in sorted order**. The accounts themselves can be returned in **any order**.
+After merging the accounts, return the accounts in the following format: the first element of each account is the name, and the rest of the elements are emails in sorted order. The accounts themselves can be returned in any order.
## Examples
@@ -34,8 +34,8 @@ Output: [["Ethan","Ethan0@m.co","Ethan4@m.co","Ethan5@m.co"],["Gabe","Gabe0@m.co
## Constraints
-- `1 <= accounts.length <= 1000`
-- `2 <= accounts[i].length <= 10`
-- `1 <= accounts[i][j].length <= 30`
-- `accounts[i][0]` consists of English letters.
-- `accounts[i][j] (for j > 0)` is a valid email.
+- 1 <= accounts.length <= 1000
+- 2 <= accounts[i].length <= 10
+- 1 <= accounts[i][j].length <= 30
+- accounts[i][0] consists of English letters.
+- accounts[i][j] (for j > 0) is a valid email.
diff --git a/leetcode/accounts_merge/test_solution.py b/leetcode/accounts_merge/test_solution.py
index fc73296..8f18c66 100644
--- a/leetcode/accounts_merge/test_solution.py
+++ b/leetcode/accounts_merge/test_solution.py
@@ -43,64 +43,69 @@ def setup_method(self):
["Fern", "Fern0@m.co", "Fern1@m.co", "Fern5@m.co"],
],
),
- ([["John", "john@mail.com"]], [["John", "john@mail.com"]]),
+ ([["Alice", "alice@mail.com"]], [["Alice", "alice@mail.com"]]),
(
- [["John", "john1@mail.com"], ["John", "john2@mail.com"], ["John", "john3@mail.com"]],
- [["John", "john1@mail.com"], ["John", "john2@mail.com"], ["John", "john3@mail.com"]],
+ [["Bob", "bob1@mail.com"], ["Bob", "bob2@mail.com"]],
+ [["Bob", "bob1@mail.com"], ["Bob", "bob2@mail.com"]],
),
(
[
- ["John", "a@mail.com", "b@mail.com"],
- ["John", "b@mail.com", "c@mail.com"],
- ["John", "d@mail.com"],
+ ["Alice", "alice@mail.com", "alice2@mail.com"],
+ ["Alice", "alice2@mail.com", "alice3@mail.com"],
],
- [["John", "a@mail.com", "b@mail.com", "c@mail.com"], ["John", "d@mail.com"]],
+ [["Alice", "alice2@mail.com", "alice3@mail.com", "alice@mail.com"]],
),
(
[
- ["Alice", "alice@mail.com", "alice1@mail.com"],
- ["Alice", "alice2@mail.com", "alice3@mail.com"],
- ["Alice", "alice1@mail.com", "alice2@mail.com"],
+ ["A", "a@mail.com", "b@mail.com"],
+ ["B", "b@mail.com", "c@mail.com"],
+ ["C", "c@mail.com", "d@mail.com"],
],
- [["Alice", "alice1@mail.com", "alice2@mail.com", "alice3@mail.com", "alice@mail.com"]],
+ [["A", "a@mail.com", "b@mail.com", "c@mail.com", "d@mail.com"]],
+ ),
+ ([["David", "david@mail.com"], ["David", "david@mail.com"]], [["David", "david@mail.com"]]),
+ (
+ [["Alex", "alex1@mail.com"], ["Bob", "bob1@mail.com"], ["Charlie", "charlie1@mail.com"]],
+ [["Alex", "alex1@mail.com"], ["Bob", "bob1@mail.com"], ["Charlie", "charlie1@mail.com"]],
),
- ([["Bob", "bob@mail.com"], ["Bob", "bob@mail.com"]], [["Bob", "bob@mail.com"]]),
(
[
- ["David", "david1@mail.com", "david2@mail.com"],
- ["David", "david3@mail.com"],
- ["David", "david2@mail.com", "david4@mail.com"],
+ ["John", "john1@mail.com", "john2@mail.com"],
+ ["John", "john3@mail.com"],
+ ["Jane", "jane1@mail.com"],
],
[
- ["David", "david1@mail.com", "david2@mail.com", "david4@mail.com"],
- ["David", "david3@mail.com"],
+ ["John", "john1@mail.com", "john2@mail.com"],
+ ["John", "john3@mail.com"],
+ ["Jane", "jane1@mail.com"],
],
),
(
[
- ["Alex", "alex@mail.com"],
- ["Alex", "alex@mail.com", "alex2@mail.com"],
- ["Alex", "alex3@mail.com"],
+ ["User", "user@mail.com", "user1@mail.com"],
+ ["User", "user2@mail.com", "user@mail.com"],
+ ["User", "user3@mail.com", "user1@mail.com"],
],
- [["Alex", "alex2@mail.com", "alex@mail.com"], ["Alex", "alex3@mail.com"]],
+ [["User", "user1@mail.com", "user2@mail.com", "user3@mail.com", "user@mail.com"]],
),
(
[
- ["Tom", "tom1@mail.com"],
- ["Tom", "tom2@mail.com"],
- ["Tom", "tom3@mail.com"],
- ["Tom", "tom4@mail.com"],
- ],
- [
- ["Tom", "tom1@mail.com"],
- ["Tom", "tom2@mail.com"],
- ["Tom", "tom3@mail.com"],
- ["Tom", "tom4@mail.com"],
+ ["Test", "test1@mail.com"],
+ ["Test", "test2@mail.com"],
+ ["Test", "test1@mail.com", "test3@mail.com"],
],
+ [["Test", "test2@mail.com"], ["Test", "test1@mail.com", "test3@mail.com"]],
),
(
- [["Sam", "sam@mail.com", "sam1@mail.com", "sam2@mail.com"]],
- [["Sam", "sam@mail.com", "sam1@mail.com", "sam2@mail.com"]],
+ [
+ ["Name", "a@mail.com", "b@mail.com", "c@mail.com"],
+ ["Name", "d@mail.com", "e@mail.com"],
+ ["Name", "c@mail.com", "f@mail.com"],
+ ],
+ [
+ ["Name", "d@mail.com", "e@mail.com"],
+ ["Name", "a@mail.com", "b@mail.com", "c@mail.com", "f@mail.com"],
+ ],
),
],
)
diff --git a/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/README.md b/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/README.md
new file mode 100644
index 0000000..1fff676
--- /dev/null
+++ b/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/README.md
@@ -0,0 +1,39 @@
+# Construct Binary Tree from Preorder and Inorder Traversal
+
+**Difficulty:** Medium
+**Topics:** Array, Hash Table, Divide and Conquer, Tree, Binary Tree
+**Tags:** grind-75
+
+**LeetCode:** [Problem 105](https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/description/)
+
+## Problem Description
+
+Given two integer arrays `preorder` and `inorder` where `preorder` is the preorder traversal of a binary tree and `inorder` is the inorder traversal of the same tree, construct and return the binary tree.
+
+## Examples
+
+### Example 1:
+
+
+
+```
+Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
+Output: [3,9,20,null,null,15,7]
+```
+
+### Example 2:
+
+```
+Input: preorder = [-1], inorder = [-1]
+Output: [-1]
+```
+
+## Constraints
+
+- 1 <= preorder.length <= 3000
+- inorder.length == preorder.length
+- -3000 <= preorder[i], inorder[i] <= 3000
+- preorder and inorder consist of unique values.
+- Each value of inorder also appears in preorder.
+- preorder is guaranteed to be the preorder traversal of the tree.
+- inorder is guaranteed to be the inorder traversal of the tree.
diff --git a/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/__init__.py b/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/helpers.py b/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/helpers.py
new file mode 100644
index 0000000..10c4f04
--- /dev/null
+++ b/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/helpers.py
@@ -0,0 +1,12 @@
+from leetcode_py import TreeNode
+
+
+def run_build_tree(solution_class: type, preorder: list[int], inorder: list[int]):
+ implementation = solution_class()
+ return implementation.build_tree(preorder, inorder)
+
+
+def assert_build_tree(result: TreeNode | None, expected_list: list[int | None]) -> bool:
+ expected = TreeNode.from_list(expected_list) if expected_list else None
+ assert result == expected
+ return True
diff --git a/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/playground.py b/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/playground.py
new file mode 100644
index 0000000..fa4baf4
--- /dev/null
+++ b/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_build_tree, run_build_tree
+from solution import Solution
+
+# %%
+# Example test case
+preorder = [3, 9, 20, 15, 7]
+inorder = [9, 3, 15, 20, 7]
+expected_list = [3, 9, 20, None, None, 15, 7]
+
+# %%
+result = run_build_tree(Solution, preorder, inorder)
+result
+
+# %%
+assert_build_tree(result, expected_list)
diff --git a/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/solution.py b/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/solution.py
new file mode 100644
index 0000000..8dce67c
--- /dev/null
+++ b/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/solution.py
@@ -0,0 +1,59 @@
+from leetcode_py import TreeNode
+
+
+class Solution:
+ """
+ Construct Binary Tree from Preorder and Inorder Traversal
+
+ Algorithm Explanation:
+ - Preorder: Root -> Left -> Right (first element is always root)
+ - Inorder: Left -> Root -> Right (root splits left/right subtrees)
+
+ Example: preorder=[3,9,20,15,7], inorder=[9,3,15,20,7]
+
+ Step 1: Root = 3 (first in preorder)
+ Find 3 in inorder at index 1
+ Left subtree: inorder[0:1] = [9]
+ Right subtree: inorder[2:] = [15,20,7]
+
+ Step 2: Build left subtree with preorder=[9], inorder=[9]
+ Root = 9, no children
+
+ Step 3: Build right subtree with preorder=[20,15,7], inorder=[15,20,7]
+ Root = 20, left=[15], right=[7]
+
+ Final tree:
+ 3
+ / \
+ 9 20
+ / \
+ 15 7
+ """
+
+ # Time: O(n) - hashmap lookup O(1) for each of n nodes
+ # Space: O(n) - hashmap + recursion stack
+ def build_tree(self, preorder: list[int], inorder: list[int]) -> TreeNode | None:
+ if not preorder or not inorder:
+ return None
+
+ inorder_map = {val: i for i, val in enumerate(inorder)}
+ self.preorder_index = 0
+
+ def build(left: int, right: int) -> TreeNode | None:
+ # left, right: boundaries in inorder array for current subtree
+ if left > right:
+ return None
+
+ root_val = preorder[self.preorder_index]
+ self.preorder_index += 1
+ root = TreeNode(root_val)
+
+ mid = inorder_map[root_val] # root position in inorder
+ # Left subtree: inorder[left:mid-1]
+ root.left = build(left, mid - 1)
+ # Right subtree: inorder[mid+1:right]
+ root.right = build(mid + 1, right)
+
+ return root
+
+ return build(0, len(inorder) - 1)
diff --git a/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/test_solution.py b/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/test_solution.py
new file mode 100644
index 0000000..5f08fe6
--- /dev/null
+++ b/leetcode/construct_binary_tree_from_preorder_and_inorder_traversal/test_solution.py
@@ -0,0 +1,41 @@
+import pytest
+
+from leetcode_py.test_utils import logged_test
+
+from .helpers import assert_build_tree, run_build_tree
+from .solution import Solution
+
+
+class TestConstructBinaryTreeFromPreorderAndInorderTraversal:
+ def setup_method(self):
+ self.solution = Solution()
+
+ @logged_test
+ @pytest.mark.parametrize(
+ "preorder, inorder, expected_list",
+ [
+ ([], [], []),
+ ([1], [1], [1]),
+ ([3, 9, 20, 15, 7], [9, 3, 15, 20, 7], [3, 9, 20, None, None, 15, 7]),
+ ([-1], [-1], [-1]),
+ ([1, 2], [2, 1], [1, 2]),
+ ([1, 2], [1, 2], [1, None, 2]),
+ ([1, 2, 3], [2, 1, 3], [1, 2, 3]),
+ ([1, 2, 4, 5, 3, 6], [4, 2, 5, 1, 6, 3], [1, 2, 3, 4, 5, 6]),
+ ([1, 2, 3, 4], [1, 2, 3, 4], [1, None, 2, None, 3, None, 4]),
+ ([4, 3, 2, 1], [1, 2, 3, 4], [4, 3, None, 2, None, 1]),
+ ([10, 5, 1, 7, 40, 50], [1, 5, 7, 10, 40, 50], [10, 5, 40, 1, 7, None, 50]),
+ ([1, 3, 2], [1, 2, 3], [1, None, 3, 2]),
+ ([2, 1, 3], [1, 2, 3], [2, 1, 3]),
+ ([5, 3, 2, 1, 4, 6, 7], [1, 2, 3, 4, 5, 6, 7], [5, 3, 6, 2, 4, None, 7, 1]),
+ (
+ [7, 3, 2, 1, 5, 4, 6, 10, 9, 11],
+ [1, 2, 3, 4, 5, 6, 7, 9, 10, 11],
+ [7, 3, 10, 2, 5, 9, 11, 1, None, 4, 6],
+ ),
+ ([-3000, -2999, -2998], [-2998, -2999, -3000], [-3000, -2999, None, -2998]),
+ ],
+ )
+ def test_build_tree(self, preorder: list[int], inorder: list[int], expected_list: list[int | None]):
+ result = run_build_tree(Solution, preorder, inorder)
+ assert_build_tree(result, expected_list)
diff --git a/leetcode/diagonal_traverse/README.md b/leetcode/diagonal_traverse/README.md
new file mode 100644
index 0000000..e988eef
--- /dev/null
+++ b/leetcode/diagonal_traverse/README.md
@@ -0,0 +1,37 @@
+# Diagonal Traverse
+
+**Difficulty:** Medium
+**Topics:** Array, Matrix, Simulation
+**Tags:**
+
+**LeetCode:** [Problem 498](https://leetcode.com/problems/diagonal-traverse/description/)
+
+## Problem Description
+
+Given an `m x n` matrix `mat`, return _an array of all the elements of the array in a diagonal order_.
+
+## Examples
+
+### Example 1:
+
+
+
+```
+Input: mat = [[1,2,3],[4,5,6],[7,8,9]]
+Output: [1,2,4,7,5,3,6,8,9]
+```
+
+### Example 2:
+
+```
+Input: mat = [[1,2],[3,4]]
+Output: [1,2,3,4]
+```
+
+## Constraints
+
+- `m == mat.length`
+- `n == mat[i].length`
+- `1 <= m, n <= 10^4`
+- `1 <= m * n <= 10^4`
+- `-10^5 <= mat[i][j] <= 10^5`
diff --git a/leetcode/diagonal_traverse/__init__.py b/leetcode/diagonal_traverse/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/leetcode/diagonal_traverse/helpers.py b/leetcode/diagonal_traverse/helpers.py
new file mode 100644
index 0000000..e828640
--- /dev/null
+++ b/leetcode/diagonal_traverse/helpers.py
@@ -0,0 +1,8 @@
+def run_find_diagonal_order(solution_class: type, mat: list[list[int]]):
+ implementation = solution_class()
+ return implementation.find_diagonal_order(mat)
+
+
+def assert_find_diagonal_order(result: list[int], expected: list[int]) -> bool:
+ assert result == expected
+ return True
diff --git a/leetcode/diagonal_traverse/playground.py b/leetcode/diagonal_traverse/playground.py
new file mode 100644
index 0000000..ffdfc99
--- /dev/null
+++ b/leetcode/diagonal_traverse/playground.py
@@ -0,0 +1,29 @@
+# ---
+# jupyter:
+# jupytext:
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_find_diagonal_order, run_find_diagonal_order
+from solution import Solution
+
+# %%
+# Example test case
+mat = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
+expected = [1, 2, 4, 7, 5, 3, 6, 8, 9]
+
+# %%
+result = run_find_diagonal_order(Solution, mat)
+result
+
+# %%
+assert_find_diagonal_order(result, expected)
diff --git a/leetcode/diagonal_traverse/solution.py b/leetcode/diagonal_traverse/solution.py
new file mode 100644
index 0000000..e3f8ecb
--- /dev/null
+++ b/leetcode/diagonal_traverse/solution.py
@@ -0,0 +1,70 @@
+class Solution:
+ """
+ Diagonal Traverse Pattern:
+
+ Matrix with coordinates: d = i+j (diagonal index):
+ 1(0,0) 2(0,1) 3(0,2) d=0: 1(0,0) ↗
+ 4(1,0) 5(1,1) 6(1,2) d=1: 2(0,1), 4(1,0) ↙
+ 7(2,0) 8(2,1) 9(2,2) d=2: 3(0,2), 5(1,1), 7(2,0) ↗
+ d=3: 6(1,2), 8(2,1) ↙
+ d=4: 9(2,2) ↗
+
+ 'd' = diagonal number = sum of row+col indices (i+j)
+ Each diagonal contains elements where i+j equals the same value
+
+ Result: [1,2,4,7,5,3,6,8,9]
+ """
+
+ # Time: O(m*n)
+ # Space: O(1)
+ def find_diagonal_order(self, mat: list[list[int]]) -> list[int]:
+ m, n = len(mat), len(mat[0])
+ result = []
+
+ for d in range(m + n - 1):
+ if d % 2 == 0: # up-right diagonal
+ for i in range(min(d, m - 1), max(-1, d - n), -1):
+ result.append(mat[i][d - i])
+ else: # down-left diagonal
+ for i in range(max(0, d - n + 1), min(d + 1, m)):
+ result.append(mat[i][d - i])
+
+ return result
+
+
+class SolutionRowShift:
+ """
+ Row-shift approach: shift each row to align diagonals into columns
+
+ Original matrix: After shifting rows (col-row=actual_col):
+ 1 2 3 col=0 col=1 col=2 col=3 col=4
+ 4 5 6 1 2 3
+ 7 8 9 4 5 6
+ 7 8 9
+ ↑ ↓ ↑ ↓ ↑
+
+ Each row is shifted right by its row index, creating vertical columns
+ from the original diagonals. Then alternate traversal direction.
+
+ Traverse: 1 → 2,4 → 7,5,3 → 6,8 → 9
+ """
+
+ # Time: O(m*n)
+ # Space: O(1)
+ def find_diagonal_order(self, mat: list[list[int]]) -> list[int]:
+ m, n = len(mat), len(mat[0])
+ result = []
+
+ for col in range(m + n - 1):
+ if col % 2 == 1: # upward
+ for row in range(m):
+ i = col - row
+ if 0 <= i < n:
+ result.append(mat[row][i])
+ else: # downward
+ for row in range(m - 1, -1, -1):
+ i = col - row
+ if 0 <= i < n:
+ result.append(mat[row][i])
+
+ return result
diff --git a/leetcode/diagonal_traverse/test_solution.py b/leetcode/diagonal_traverse/test_solution.py
new file mode 100644
index 0000000..a57c110
--- /dev/null
+++ b/leetcode/diagonal_traverse/test_solution.py
@@ -0,0 +1,34 @@
+import pytest
+
+from leetcode_py.test_utils import logged_test
+
+from .helpers import assert_find_diagonal_order, run_find_diagonal_order
+from .solution import Solution, SolutionRowShift
+
+
+class TestDiagonalTraverse:
+ def setup_method(self):
+ self.solution = Solution()
+
+ @logged_test
+ @pytest.mark.parametrize("solution_class", [Solution, SolutionRowShift])
+ @pytest.mark.parametrize(
+ "mat, expected",
+ [
+ ([[1, 2, 3], [4, 5, 6], [7, 8, 9]], [1, 2, 4, 7, 5, 3, 6, 8, 9]),
+ ([[1, 2], [3, 4]], [1, 2, 3, 4]),
+ ([[1]], [1]),
+ ([[1, 2, 3]], [1, 2, 3]),
+ ([[1], [2], [3]], [1, 2, 3]),
+ ([[1, 2, 3, 4], [5, 6, 7, 8]], [1, 2, 5, 6, 3, 4, 7, 8]),
+ ([[1, 2], [3, 4], [5, 6]], [1, 2, 3, 5, 4, 6]),
+ ([[1, 2, 3, 4, 5]], [1, 2, 3, 4, 5]),
+ ([[1], [2], [3], [4], [5]], [1, 2, 3, 4, 5]),
+ ([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]], [1, 2, 5, 9, 6, 3, 4, 7, 10, 11, 8, 12]),
+ ([[-1, 0, 1], [2, -3, 4]], [-1, 0, 2, -3, 1, 4]),
+ ([[100]], [100]),
+ ],
+ )
+ def test_find_diagonal_order(self, solution_class, mat: list[list[int]], expected: list[int]):
+ result = run_find_diagonal_order(solution_class, mat)
+ assert_find_diagonal_order(result, expected)
diff --git a/leetcode/find_all_anagrams_in_a_string/README.md b/leetcode/find_all_anagrams_in_a_string/README.md
new file mode 100644
index 0000000..d84fab3
--- /dev/null
+++ b/leetcode/find_all_anagrams_in_a_string/README.md
@@ -0,0 +1,43 @@
+# Find All Anagrams in a String
+
+**Difficulty:** Medium
+**Topics:** Hash Table, String, Sliding Window
+**Tags:** grind-75
+
+**LeetCode:** [Problem 438](https://leetcode.com/problems/find-all-anagrams-in-a-string/description/)
+
+## Problem Description
+
+Given two strings `s` and `p`, return an array of all the start indices of `p`'s anagrams in `s`. You may return the answer in any order.
+
+An **anagram** is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.
+
+## Examples
+
+### Example 1:
+
+```
+Input: s = "cbaebabacd", p = "abc"
+Output: [0,6]
+```
+
+**Explanation:**
+The substring with start index = 0 is "cba", which is an anagram of "abc".
+The substring with start index = 6 is "bac", which is an anagram of "abc".
+
+### Example 2:
+
+```
+Input: s = "abab", p = "ab"
+Output: [0,1,2]
+```
+
+**Explanation:**
+The substring with start index = 0 is "ab", which is an anagram of "ab".
+The substring with start index = 1 is "ba", which is an anagram of "ab".
+The substring with start index = 2 is "ab", which is an anagram of "ab".
+
+## Constraints
+
+- 1 <= s.length, p.length <= 3 \* 10^4
+- s and p consist of lowercase English letters.
diff --git a/leetcode/find_all_anagrams_in_a_string/__init__.py b/leetcode/find_all_anagrams_in_a_string/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/leetcode/find_all_anagrams_in_a_string/helpers.py b/leetcode/find_all_anagrams_in_a_string/helpers.py
new file mode 100644
index 0000000..5836a99
--- /dev/null
+++ b/leetcode/find_all_anagrams_in_a_string/helpers.py
@@ -0,0 +1,8 @@
+def run_find_anagrams(solution_class: type, s: str, p: str):
+ implementation = solution_class()
+ return implementation.find_anagrams(s, p)
+
+
+def assert_find_anagrams(result: list[int], expected: list[int]) -> bool:
+ assert sorted(result) == sorted(expected)
+ return True
diff --git a/leetcode/find_all_anagrams_in_a_string/playground.py b/leetcode/find_all_anagrams_in_a_string/playground.py
new file mode 100644
index 0000000..42f330b
--- /dev/null
+++ b/leetcode/find_all_anagrams_in_a_string/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_find_anagrams, run_find_anagrams
+from solution import Solution
+
+# %%
+# Example test case
+s = "cbaebabacd"
+p = "abc"
+expected = [0, 6]
+
+# %%
+result = run_find_anagrams(Solution, s, p)
+result
+
+# %%
+assert_find_anagrams(result, expected)
diff --git a/leetcode/find_all_anagrams_in_a_string/solution.py b/leetcode/find_all_anagrams_in_a_string/solution.py
new file mode 100644
index 0000000..f26a54f
--- /dev/null
+++ b/leetcode/find_all_anagrams_in_a_string/solution.py
@@ -0,0 +1,56 @@
+from collections import Counter
+
+
+class Solution:
+ """
+ Sliding Window with Character Frequency Counting
+
+ Algorithm:
+ 1. Count character frequencies in pattern p
+ 2. Use sliding window of size len(p) on string s
+ 3. Maintain frequency count of current window
+ 4. When frequencies match, record start index
+
+ ASCII Visualization:
+ s = "cbaebabacd", p = "abc" (need: a=1, b=1, c=1)
+
+ Window positions:
+ [cba]ebabacd -> {c:1, b:1, a:1} ✓ matches -> index 0
+ c[bae]babacd -> {b:1, a:1, e:1} ✗
+ cb[aeb]abacd -> {a:1, e:1, b:1} ✗
+ cba[eba]bacd -> {e:1, b:1, a:1} ✗
+ cbae[bab]acd -> {b:2, a:1} ✗
+ cbaeb[aba]cd -> {a:2, b:1} ✗
+ cbaeba[bac]d -> {b:1, a:1, c:1} ✓ matches -> index 6
+ """
+
+ # Time: O(n) where n is length of s
+ # Space: O(1) - at most 26 lowercase letters
+ def find_anagrams(self, s: str, p: str) -> list[int]:
+ if len(p) > len(s):
+ return []
+
+ result = []
+ p_count = Counter(p)
+ window_count = Counter(s[: len(p)])
+
+ # Check first window
+ if window_count == p_count:
+ result.append(0)
+
+ # Slide window
+ for i in range(len(p), len(s)):
+ # Add new character
+ window_count[s[i]] += 1
+
+ # Remove old character
+ left_char = s[i - len(p)]
+ window_count[left_char] -= 1
+ if window_count[left_char] == 0:
+ del window_count[left_char]
+
+ # Check if current window is anagram
+ if window_count == p_count:
+ result.append(i - len(p) + 1)
+
+ return result
diff --git a/leetcode/find_all_anagrams_in_a_string/test_solution.py b/leetcode/find_all_anagrams_in_a_string/test_solution.py
new file mode 100644
index 0000000..3782888
--- /dev/null
+++ b/leetcode/find_all_anagrams_in_a_string/test_solution.py
@@ -0,0 +1,33 @@
+import pytest
+
+from leetcode_py.test_utils import logged_test
+
+from .helpers import assert_find_anagrams, run_find_anagrams
+from .solution import Solution
+
+
+class TestFindAllAnagramsInAString:
+ def setup_method(self):
+ self.solution = Solution()
+
+ @logged_test
+ @pytest.mark.parametrize(
+ "s, p, expected",
+ [
+ ("cbaebabacd", "abc", [0, 6]),
+ ("abab", "ab", [0, 1, 2]),
+ ("a", "aa", []),
+ ("aa", "aa", [0]),
+ ("abcdefg", "xyz", []),
+ ("aab", "ab", [1]),
+ ("aaab", "ab", [2]),
+ ("baa", "aa", [1]),
+ ("abacabad", "aaab", []),
+ ("ababacb", "abc", [3, 4]),
+ ("abaacbabc", "abc", [3, 4, 6]),
+ ("abab", "abab", [0]),
+ ],
+ )
+ def test_find_anagrams(self, s: str, p: str, expected: list[int]):
+ result = run_find_anagrams(Solution, s, p)
+ assert_find_anagrams(result, expected)
diff --git a/leetcode/letter_combinations_of_a_phone_number/README.md b/leetcode/letter_combinations_of_a_phone_number/README.md
new file mode 100644
index 0000000..43f0064
--- /dev/null
+++ b/leetcode/letter_combinations_of_a_phone_number/README.md
@@ -0,0 +1,43 @@
+# Letter Combinations of a Phone Number
+
+**Difficulty:** Medium
+**Topics:** Hash Table, String, Backtracking
+**Tags:** grind-75
+
+**LeetCode:** [Problem 17](https://leetcode.com/problems/letter-combinations-of-a-phone-number/description/)
+
+## Problem Description
+
+Given a string containing digits from `2-9` inclusive, return all possible letter combinations that the number could represent. Return the answer in **any order**.
+
+A mapping of digits to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.
+
+## Examples
+
+### Example 1:
+
+
+
+```
+Input: digits = "23"
+Output: ["ad","ae","af","bd","be","bf","cd","ce","cf"]
+```
+
+### Example 2:
+
+```
+Input: digits = ""
+Output: []
+```
+
+### Example 3:
+
+```
+Input: digits = "2"
+Output: ["a","b","c"]
+```
+
+## Constraints
+
+- `0 <= digits.length <= 4`
+- `digits[i]` is a digit in the range `['2', '9']`.
diff --git a/leetcode/letter_combinations_of_a_phone_number/__init__.py b/leetcode/letter_combinations_of_a_phone_number/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/leetcode/letter_combinations_of_a_phone_number/helpers.py b/leetcode/letter_combinations_of_a_phone_number/helpers.py
new file mode 100644
index 0000000..b35cae8
--- /dev/null
+++ b/leetcode/letter_combinations_of_a_phone_number/helpers.py
@@ -0,0 +1,10 @@
+def run_letter_combinations(solution_class: type, digits: str):
+ implementation = solution_class()
+ return implementation.letter_combinations(digits)
+
+
+def assert_letter_combinations(result: list[str], expected: list[str]) -> bool:
+ result.sort()
+ expected.sort()
+ assert result == expected
+ return True
diff --git a/leetcode/letter_combinations_of_a_phone_number/playground.py b/leetcode/letter_combinations_of_a_phone_number/playground.py
new file mode 100644
index 0000000..3dcbf29
--- /dev/null
+++ b/leetcode/letter_combinations_of_a_phone_number/playground.py
@@ -0,0 +1,29 @@
+# ---
+# jupyter:
+# jupytext:
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_letter_combinations, run_letter_combinations
+from solution import Solution
+
+# %%
+# Example test case
+digits = "23"
+expected = ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]
+
+# %%
+result = run_letter_combinations(Solution, digits)
+result
+
+# %%
+assert_letter_combinations(result, expected)
diff --git a/leetcode/letter_combinations_of_a_phone_number/solution.py b/leetcode/letter_combinations_of_a_phone_number/solution.py
new file mode 100644
index 0000000..b198a2e
--- /dev/null
+++ b/leetcode/letter_combinations_of_a_phone_number/solution.py
@@ -0,0 +1,31 @@
+class Solution:
+
+ # Time: O(4^n)
+ # Space: O(4^n)
+ def letter_combinations(self, digits: str) -> list[str]:
+ if not digits:
+ return []
+
+ phone = {
+ "2": "abc",
+ "3": "def",
+ "4": "ghi",
+ "5": "jkl",
+ "6": "mno",
+ "7": "pqrs",
+ "8": "tuv",
+ "9": "wxyz",
+ }
+
+ result = []
+
+ def backtrack(i: int, path: str) -> None:
+ if i == len(digits):
+ result.append(path)
+ return
+
+ for letter in phone[digits[i]]:
+ backtrack(i + 1, path + letter)
+
+ backtrack(0, "")
+ return result
diff --git a/leetcode/letter_combinations_of_a_phone_number/test_solution.py b/leetcode/letter_combinations_of_a_phone_number/test_solution.py
new file mode 100644
index 0000000..c28498c
--- /dev/null
+++ b/leetcode/letter_combinations_of_a_phone_number/test_solution.py
@@ -0,0 +1,169 @@
+import pytest
+
+from leetcode_py.test_utils import logged_test
+
+from .helpers import assert_letter_combinations, run_letter_combinations
+from .solution import Solution
+
+
+class TestLetterCombinationsOfAPhoneNumber:
+ def setup_method(self):
+ self.solution = Solution()
+
+ @logged_test
+ @pytest.mark.parametrize(
+ "digits, expected",
+ [
+ ("23", ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]),
+ ("", []),
+ ("2", ["a", "b", "c"]),
+ (
+ "234",
+ [
+ "adg",
+ "adh",
+ "adi",
+ "aeg",
+ "aeh",
+ "aei",
+ "afg",
+ "afh",
+ "afi",
+ "bdg",
+ "bdh",
+ "bdi",
+ "beg",
+ "beh",
+ "bei",
+ "bfg",
+ "bfh",
+ "bfi",
+ "cdg",
+ "cdh",
+ "cdi",
+ "ceg",
+ "ceh",
+ "cei",
+ "cfg",
+ "cfh",
+ "cfi",
+ ],
+ ),
+ ("7", ["p", "q", "r", "s"]),
+ ("9", ["w", "x", "y", "z"]),
+ (
+ "79",
+ [
+ "pw",
+ "px",
+ "py",
+ "pz",
+ "qw",
+ "qx",
+ "qy",
+ "qz",
+ "rw",
+ "rx",
+ "ry",
+ "rz",
+ "sw",
+ "sx",
+ "sy",
+ "sz",
+ ],
+ ),
+ ("22", ["aa", "ab", "ac", "ba", "bb", "bc", "ca", "cb", "cc"]),
+ (
+ "3456",
+ [
+ "dgjm",
+ "dgjn",
+ "dgjo",
+ "dgkm",
+ "dgkn",
+ "dgko",
+ "dglm",
+ "dgln",
+ "dglo",
+ "dhjm",
+ "dhjn",
+ "dhjo",
+ "dhkm",
+ "dhkn",
+ "dhko",
+ "dhlm",
+ "dhln",
+ "dhlo",
+ "dijm",
+ "dijn",
+ "dijo",
+ "dikm",
+ "dikn",
+ "diko",
+ "dilm",
+ "diln",
+ "dilo",
+ "egjm",
+ "egjn",
+ "egjo",
+ "egkm",
+ "egkn",
+ "egko",
+ "eglm",
+ "egln",
+ "eglo",
+ "ehjm",
+ "ehjn",
+ "ehjo",
+ "ehkm",
+ "ehkn",
+ "ehko",
+ "ehlm",
+ "ehln",
+ "ehlo",
+ "eijm",
+ "eijn",
+ "eijo",
+ "eikm",
+ "eikn",
+ "eiko",
+ "eilm",
+ "eiln",
+ "eilo",
+ "fgjm",
+ "fgjn",
+ "fgjo",
+ "fgkm",
+ "fgkn",
+ "fgko",
+ "fglm",
+ "fgln",
+ "fglo",
+ "fhjm",
+ "fhjn",
+ "fhjo",
+ "fhkm",
+ "fhkn",
+ "fhko",
+ "fhlm",
+ "fhln",
+ "fhlo",
+ "fijm",
+ "fijn",
+ "fijo",
+ "fikm",
+ "fikn",
+ "fiko",
+ "film",
+ "filn",
+ "filo",
+ ],
+ ),
+ ("25", ["aj", "ak", "al", "bj", "bk", "bl", "cj", "ck", "cl"]),
+ ("78", ["pt", "pu", "pv", "qt", "qu", "qv", "rt", "ru", "rv", "st", "su", "sv"]),
+ ("89", ["tw", "tx", "ty", "tz", "uw", "ux", "uy", "uz", "vw", "vx", "vy", "vz"]),
+ ],
+ )
+ def test_letter_combinations(self, digits: str, expected: list[str]):
+ result = run_letter_combinations(Solution, digits)
+ assert_letter_combinations(result, expected)
diff --git a/leetcode/subsets/README.md b/leetcode/subsets/README.md
new file mode 100644
index 0000000..a28b3e0
--- /dev/null
+++ b/leetcode/subsets/README.md
@@ -0,0 +1,35 @@
+# Subsets
+
+**Difficulty:** Medium
+**Topics:** Array, Backtracking, Bit Manipulation
+**Tags:** grind-75
+
+**LeetCode:** [Problem 78](https://leetcode.com/problems/subsets/description/)
+
+## Problem Description
+
+Given an integer array `nums` of **unique** elements, return _all possible_ subsets (the power set).
+
+The solution set **must not** contain duplicate subsets. Return the solution in **any order**.
+
+## Examples
+
+### Example 1:
+
+```
+Input: nums = [1,2,3]
+Output: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
+```
+
+### Example 2:
+
+```
+Input: nums = [0]
+Output: [[],[0]]
+```
+
+## Constraints
+
+- 1 <= nums.length <= 10
+- -10 <= nums[i] <= 10
+- All the numbers of nums are unique.
diff --git a/leetcode/subsets/__init__.py b/leetcode/subsets/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/leetcode/subsets/helpers.py b/leetcode/subsets/helpers.py
new file mode 100644
index 0000000..a28c5a3
--- /dev/null
+++ b/leetcode/subsets/helpers.py
@@ -0,0 +1,11 @@
+def run_subsets(solution_class: type, nums: list[int]):
+ implementation = solution_class()
+ return implementation.subsets(nums)
+
+
+def assert_subsets(result: list[list[int]], expected: list[list[int]]) -> bool:
+ # Sort both result and expected for comparison since order doesn't matter
+ result_sorted = [sorted(subset) for subset in sorted(result)]
+ expected_sorted = [sorted(subset) for subset in sorted(expected)]
+ assert result_sorted == expected_sorted
+ return True
diff --git a/leetcode/subsets/playground.py b/leetcode/subsets/playground.py
new file mode 100644
index 0000000..c7f57bb
--- /dev/null
+++ b/leetcode/subsets/playground.py
@@ -0,0 +1,29 @@
+# ---
+# jupyter:
+# jupytext:
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_subsets, run_subsets
+from solution import Solution
+
+# %%
+# Example test case
+nums = [1, 2, 3]
+expected = [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
+
+# %%
+result = run_subsets(Solution, nums)
+result
+
+# %%
+assert_subsets(result, expected)
diff --git a/leetcode/subsets/solution.py b/leetcode/subsets/solution.py
new file mode 100644
index 0000000..3b9d8a2
--- /dev/null
+++ b/leetcode/subsets/solution.py
@@ -0,0 +1,16 @@
+class Solution:
+
+ # Time: O(2^n)
+ # Space: O(2^n)
+ def subsets(self, nums: list[int]) -> list[list[int]]:
+ result = []
+
+ def backtrack(start: int, path: list[int]) -> None:
+ result.append(path[:])
+ for i in range(start, len(nums)):
+ path.append(nums[i])
+ backtrack(i + 1, path)
+ path.pop()
+
+ backtrack(0, [])
+ return result
diff --git a/leetcode/subsets/test_solution.py b/leetcode/subsets/test_solution.py
new file mode 100644
index 0000000..6efd136
--- /dev/null
+++ b/leetcode/subsets/test_solution.py
@@ -0,0 +1,53 @@
+import pytest
+
+from leetcode_py.test_utils import logged_test
+
+from .helpers import assert_subsets, run_subsets
+from .solution import Solution
+
+
+class TestSubsets:
+ def setup_method(self):
+ self.solution = Solution()
+
+ @logged_test
+ @pytest.mark.parametrize(
+ "nums, expected",
+ [
+ ([1, 2, 3], [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]),
+ ([0], [[], [0]]),
+ ([1], [[], [1]]),
+ ([1, 2], [[], [1], [2], [1, 2]]),
+ ([4, 1, 0], [[], [4], [4, 1], [4, 1, 0], [4, 0], [1], [1, 0], [0]]),
+ ([-1, 0, 1], [[], [-1], [-1, 0], [-1, 0, 1], [-1, 1], [0], [0, 1], [1]]),
+ ([5], [[], [5]]),
+ ([2, 1, 3], [[], [2], [2, 1], [2, 1, 3], [2, 3], [1], [1, 3], [3]]),
+ ([10], [[], [10]]),
+ ([-10, 10], [[], [-10], [-10, 10], [10]]),
+ (
+ [1, 2, 3, 4],
+ [
+ [],
+ [1],
+ [1, 2],
+ [1, 2, 3],
+ [1, 2, 3, 4],
+ [1, 2, 4],
+ [1, 3],
+ [1, 3, 4],
+ [1, 4],
+ [2],
+ [2, 3],
+ [2, 3, 4],
+ [2, 4],
+ [3],
+ [3, 4],
+ [4],
+ ],
+ ),
+ ([5, 2], [[], [5], [5, 2], [2]]),
+ ],
+ )
+ def test_subsets(self, nums: list[int], expected: list[list[int]]):
+ result = run_subsets(Solution, nums)
+ assert_subsets(result, expected)
diff --git a/leetcode/unique_paths/README.md b/leetcode/unique_paths/README.md
new file mode 100644
index 0000000..d40c811
--- /dev/null
+++ b/leetcode/unique_paths/README.md
@@ -0,0 +1,43 @@
+# Unique Paths
+
+**Difficulty:** Medium
+**Topics:** Math, Dynamic Programming, Combinatorics
+**Tags:** grind-75
+
+**LeetCode:** [Problem 62](https://leetcode.com/problems/unique-paths/description/)
+
+## Problem Description
+
+There is a robot on an `m x n` grid. The robot is initially located at the **top-left corner** (i.e., `grid[0][0]`). The robot tries to move to the **bottom-right corner** (i.e., `grid[m - 1][n - 1]`). The robot can only move either down or right at any point in time.
+
+Given the two integers `m` and `n`, return _the number of possible unique paths that the robot can take to reach the bottom-right corner_.
+
+The test cases are generated so that the answer will be less than or equal to `2 * 10^9`.
+
+## Examples
+
+### Example 1:
+
+
+
+```
+Input: m = 3, n = 7
+Output: 28
+```
+
+### Example 2:
+
+```
+Input: m = 3, n = 2
+Output: 3
+```
+
+**Explanation:** From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
+
+1. Right -> Down -> Down
+2. Down -> Down -> Right
+3. Down -> Right -> Down
+
+## Constraints
+
+- 1 <= m, n <= 100
diff --git a/leetcode/unique_paths/__init__.py b/leetcode/unique_paths/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/leetcode/unique_paths/helpers.py b/leetcode/unique_paths/helpers.py
new file mode 100644
index 0000000..c0ead78
--- /dev/null
+++ b/leetcode/unique_paths/helpers.py
@@ -0,0 +1,8 @@
+def run_unique_paths(solution_class: type, m: int, n: int):
+ implementation = solution_class()
+ return implementation.unique_paths(m, n)
+
+
+def assert_unique_paths(result: int, expected: int) -> bool:
+ assert result == expected
+ return True
diff --git a/leetcode/unique_paths/playground.py b/leetcode/unique_paths/playground.py
new file mode 100644
index 0000000..e70351b
--- /dev/null
+++ b/leetcode/unique_paths/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_unique_paths, run_unique_paths
+from solution import Solution
+
+# %%
+# Example test case
+m = 3
+n = 7
+expected = 28
+
+# %%
+result = run_unique_paths(Solution, m, n)
+result
+
+# %%
+assert_unique_paths(result, expected)
diff --git a/leetcode/unique_paths/solution.py b/leetcode/unique_paths/solution.py
new file mode 100644
index 0000000..0a3ddb9
--- /dev/null
+++ b/leetcode/unique_paths/solution.py
@@ -0,0 +1,29 @@
+class Solution:
+ # Dynamic Programming
+ # Time: O(m * n)
+ # Space: O(min(m, n))
+ def unique_paths(self, m: int, n: int) -> int:
+ if m > n:
+ m, n = n, m
+ dp = [1] * m
+ for _ in range(1, n):
+ for j in range(1, m):
+ dp[j] += dp[j - 1]
+ return dp[m - 1]
+
+
+class SolutionMath:
+
+ # Math solution: C(m+n-2, m-1) = (m+n-2)! / ((m-1)! * (n-1)!)
+ # Time: O(min(m, n))
+ # Space: O(1)
+ def unique_paths(self, m: int, n: int) -> int:
+ # Total moves: (m-1) right + (n-1) down = m+n-2
+ # Choose (m-1) positions for right moves out of (m+n-2) total
+ if m > n:
+ m, n = n, m # Optimize for smaller factorial
+
+ result = 1
+ for i in range(m - 1):
+ result = result * (n + i) // (i + 1)
+ return result
diff --git a/leetcode/unique_paths/test_solution.py b/leetcode/unique_paths/test_solution.py
new file mode 100644
index 0000000..b66ad67
--- /dev/null
+++ b/leetcode/unique_paths/test_solution.py
@@ -0,0 +1,34 @@
+import pytest
+
+from leetcode_py.test_utils import logged_test
+
+from .helpers import assert_unique_paths, run_unique_paths
+from .solution import Solution, SolutionMath
+
+
+class TestUniquePaths:
+ @logged_test
+ @pytest.mark.parametrize("solution_class", [Solution, SolutionMath])
+ @pytest.mark.parametrize(
+ "m, n, expected",
+ [
+ (3, 7, 28),
+ (3, 2, 3),
+ (1, 1, 1),
+ (1, 10, 1),
+ (10, 1, 1),
+ (2, 2, 2),
+ (3, 3, 6),
+ (4, 4, 20),
+ (5, 5, 70),
+ (2, 3, 3),
+ (3, 4, 10),
+ (4, 5, 35),
+ (6, 3, 21),
+ (7, 3, 28),
+ (10, 10, 48620),
+ ],
+ )
+ def test_unique_paths(self, solution_class, m: int, n: int, expected: int):
+ result = run_unique_paths(solution_class, m, n)
+ assert_unique_paths(result, expected)
diff --git a/leetcode/word_search/README.md b/leetcode/word_search/README.md
new file mode 100644
index 0000000..8ceb82b
--- /dev/null
+++ b/leetcode/word_search/README.md
@@ -0,0 +1,52 @@
+# Word Search
+
+**Difficulty:** Medium
+**Topics:** Array, String, Backtracking, Depth-First Search, Matrix
+**Tags:** grind-75
+
+**LeetCode:** [Problem 79](https://leetcode.com/problems/word-search/description/)
+
+## Problem Description
+
+Given an `m x n` grid of characters `board` and a string `word`, return `true` _if_ `word` _exists in the grid_.
+
+The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than once.
+
+## Examples
+
+### Example 1:
+
+
+
+```
+Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
+Output: true
+```
+
+### Example 2:
+
+
+
+```
+Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
+Output: true
+```
+
+### Example 3:
+
+
+
+```
+Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"
+Output: false
+```
+
+## Constraints
+
+- `m == board.length`
+- `n = board[i].length`
+- `1 <= m, n <= 6`
+- `1 <= word.length <= 15`
+- `board` and `word` consists of only lowercase and uppercase English letters.
+
+**Follow up:** Could you use search pruning to make your solution faster with a larger `board`?
diff --git a/leetcode/word_search/__init__.py b/leetcode/word_search/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/leetcode/word_search/helpers.py b/leetcode/word_search/helpers.py
new file mode 100644
index 0000000..e1d096d
--- /dev/null
+++ b/leetcode/word_search/helpers.py
@@ -0,0 +1,8 @@
+def run_exist(solution_class: type, board: list[list[str]], word: str):
+ implementation = solution_class()
+ return implementation.exist(board, word)
+
+
+def assert_exist(result: bool, expected: bool) -> bool:
+ assert result == expected
+ return True
diff --git a/leetcode/word_search/playground.py b/leetcode/word_search/playground.py
new file mode 100644
index 0000000..5e69458
--- /dev/null
+++ b/leetcode/word_search/playground.py
@@ -0,0 +1,30 @@
+# ---
+# jupyter:
+# jupytext:
+# text_representation:
+# extension: .py
+# format_name: percent
+# format_version: '1.3'
+# jupytext_version: 1.17.3
+# kernelspec:
+# display_name: leetcode-py-py3.13
+# language: python
+# name: python3
+# ---
+
+# %%
+from helpers import assert_exist, run_exist
+from solution import Solution
+
+# %%
+# Example test case
+board = [["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]]
+word = "ABCCED"
+expected = True
+
+# %%
+result = run_exist(Solution, board, word)
+result
+
+# %%
+assert_exist(result, expected)
diff --git a/leetcode/word_search/solution.py b/leetcode/word_search/solution.py
new file mode 100644
index 0000000..0b5a659
--- /dev/null
+++ b/leetcode/word_search/solution.py
@@ -0,0 +1,42 @@
+from collections import Counter
+
+
+class Solution:
+
+ # Time: O(m*n*4^L) where L is word length
+ # Space: O(L)
+ def exist(self, board: list[list[str]], word: str) -> bool:
+
+ m, n = len(board), len(board[0])
+
+ # Early pruning: check if board has enough characters
+ board_counter = Counter(ch for row in board for ch in row)
+ word_counter = Counter(word)
+ for ch in word_counter:
+ if board_counter[ch] < word_counter[ch]:
+ return False
+
+ # Optimization: start from less frequent end
+ if board_counter[word[0]] > board_counter[word[-1]]:
+ word = word[::-1]
+
+ def dfs(i: int, j: int, k: int) -> bool:
+ if k == len(word):
+ return True
+ if i < 0 or i >= m or j < 0 or j >= n or board[i][j] != word[k]:
+ return False
+
+ temp = board[i][j]
+ board[i][j] = "#"
+ for di, dj in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
+ if dfs(i + di, j + dj, k + 1):
+ board[i][j] = temp
+ return True
+ board[i][j] = temp
+ return False
+
+ for i in range(m):
+ for j in range(n):
+ if dfs(i, j, 0):
+ return True
+ return False
diff --git a/leetcode/word_search/test_solution.py b/leetcode/word_search/test_solution.py
new file mode 100644
index 0000000..5e1151d
--- /dev/null
+++ b/leetcode/word_search/test_solution.py
@@ -0,0 +1,44 @@
+import pytest
+
+from leetcode_py.test_utils import logged_test
+
+from .helpers import assert_exist, run_exist
+from .solution import Solution
+
+
+class TestWordSearch:
+ def setup_method(self):
+ self.solution = Solution()
+
+ @logged_test
+ @pytest.mark.parametrize(
+ "board, word, expected",
+ [
+ ([["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]], "ABCCED", True),
+ ([["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]], "SEE", True),
+ ([["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]], "ABCB", False),
+ ([["A"]], "A", True),
+ ([["A"]], "B", False),
+ ([["A", "B"], ["C", "D"]], "ACDB", True),
+ ([["A", "B"], ["C", "D"]], "ABDC", True),
+ ([["A", "B"], ["C", "D"]], "ABCD", False),
+ ([["C", "A", "A"], ["A", "A", "A"], ["B", "C", "D"]], "AAB", True),
+ ([["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]], "ABCESEEEFS", False),
+ (
+ [
+ ["A", "A", "A", "A", "A", "A"],
+ ["A", "A", "A", "A", "A", "A"],
+ ["A", "A", "A", "A", "A", "A"],
+ ["A", "A", "A", "A", "A", "A"],
+ ["A", "A", "A", "A", "A", "A"],
+ ["A", "A", "A", "A", "A", "A"],
+ ],
+ "AAAAAAAAAAAAAAB",
+ False,
+ ),
+ ([["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]], "SFCS", True),
+ ],
+ )
+ def test_exist(self, board: list[list[str]], word: str, expected: bool):
+ result = run_exist(Solution, board, word)
+ assert_exist(result, expected)
diff --git a/pyproject.toml b/pyproject.toml
index 7306c4a..7debb1c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -76,3 +76,6 @@ python_files = ["tests.py", "test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = "-v --tb=short"
+
+[tool.coverage.run]
+omit = ["**/playground.py"]
From 71ebd05d777f476ef23dd1048c6bb36debc056d5 Mon Sep 17 00:00:00 2001
From: Wisaroot <66859294+wisarootl@users.noreply.github.com>
Date: Sat, 13 Sep 2025 12:11:00 +0700
Subject: [PATCH 04/57] docs!: update README.md (#39)
- update README.md
- add issues/PR templates
---
.github/ISSUE_TEMPLATE/bug_report.md | 28 +++
.github/ISSUE_TEMPLATE/feature_request.md | 19 ++
.github/ISSUE_TEMPLATE/problem_request.md | 13 ++
.github/PULL_REQUEST_TEMPLATE.md | 7 +
CONTRIBUTING.md | 56 ++++++
README.md | 201 ++++++++++++++--------
docs/images/linkedlist-viz.png | Bin 30293 -> 39106 bytes
7 files changed, 248 insertions(+), 76 deletions(-)
create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md
create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md
create mode 100644 .github/ISSUE_TEMPLATE/problem_request.md
create mode 100644 .github/PULL_REQUEST_TEMPLATE.md
create mode 100644 CONTRIBUTING.md
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..2071edd
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,28 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: "[BUG] "
+labels: bug
+assignees: ""
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+
+1. Run command '...'
+2. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Environment:**
+
+- OS: [e.g. macOS, Ubuntu]
+- Python version: [e.g. 3.13]
+- Poetry version: [e.g. 1.8.0]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..205ef6c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,19 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: "[FEATURE] "
+labels: enhancement
+assignees: ""
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/ISSUE_TEMPLATE/problem_request.md b/.github/ISSUE_TEMPLATE/problem_request.md
new file mode 100644
index 0000000..6338c01
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/problem_request.md
@@ -0,0 +1,13 @@
+---
+name: Problem request
+about: Request a new LeetCode problem to be added
+title: "[PROBLEM] Add LeetCode #"
+labels: problem-request
+assignees: ""
+---
+
+**Problem(s)**
+Paste problem URL(s) or list name here (LeetCode, Blind 75, NeetCode 150, etc.):
+
+**Why this problem?**
+Brief explanation of why this problem should be added (e.g. part of a study plan, important pattern, etc.)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..f41f9b3
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,7 @@
+## What changed?
+
+Describe your changes here.
+
+## Why?
+
+Brief explanation of the motivation behind this change.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..60ef591
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,56 @@
+# Contributing to LeetCode Practice Repository
+
+Thank you for your interest in contributing! This repository welcomes contributions from the community.
+
+## Ways to Contribute
+
+### 1. Add New Problems
+
+Use an LLM assistant (Cursor, GitHub Copilot Chat, Amazon Q) with the rule files:
+
+- Include `.amazonq/rules/problem-creation.md` in your LLM context
+- Ask: "Create LeetCode problem [number] ([name])"
+
+### 2. Enhance Test Cases
+
+- Include `.amazonq/rules/test-case-enhancement.md` in your LLM context
+- Ask: "Enhance test cases for [problem_name] problem"
+
+### 3. Improve Helper Classes
+
+- Add new data structure helpers in `leetcode_py/data_structures/`
+- Follow existing patterns with generic types and visualization support
+
+### 4. Bug Fixes & Improvements
+
+- Fix issues in existing problems
+- Improve documentation
+- Enhance CI/CD workflows
+
+## Development Setup
+
+```bash
+git clone https://github.com/wisarootl/leetcode-py.git
+cd leetcode-py
+poetry install
+make test
+```
+
+## Code Standards
+
+- Follow [PEP 8](https://peps.python.org/pep-0008/) Python style guide
+- Use modern type hints per [PEP 585](https://peps.python.org/pep-0585/)/[PEP 604](https://peps.python.org/pep-0604/): `list[str]`, `dict[str, int]`, `Type | None`
+- Automated linting enforced by CI (black, isort, ruff, mypy)
+- Minimum 12 test cases per problem
+
+## Pull Request Process
+
+1. Fork the repository
+2. Create a feature branch
+3. Make your changes
+4. Run `make lint` and `make test`
+5. Submit a pull request with clear description
+
+## Questions?
+
+Open an issue for questions or discussions about contributions.
diff --git a/README.md b/README.md
index ffd2a86..5ee0760 100644
--- a/README.md
+++ b/README.md
@@ -7,24 +7,38 @@
[](https://github.com/wisarootl/zerv/actions/workflows/ci-test.yml)
[](https://github.com/wisarootl/zerv/actions/workflows/cd.yml)
-Premium LeetCode practice repository with Python solutions, algorithm templates, data structure visualizations, and automated testing. Perfect for coding interview preparation, competitive programming, and mastering algorithms with Blind 75, Grind 75, and NeetCode 150 problems.
+A modern Python LeetCode practice environment that goes beyond basic problem solving. Features automated problem generation from LeetCode URLs, beautiful data structure visualizations (TreeNode, ListNode, GraphNode), and comprehensive testing with 12+ test cases per problem. Built with professional development practices including CI/CD, type hints, and quality gates.
+
+**What makes this different:**
+
+- 🤖 **LLM-Assisted Workflow**: Generate new problems instantly with AI assistance
+- 🎨 **Visual Debugging**: Interactive tree/graph rendering with Graphviz and anytree
+- 🧪 **Production Testing**: Comprehensive test suites with edge cases and reproducibility verification
+- 🚀 **Modern Python**: PEP 585/604 type hints, Poetry, and professional tooling
+- 📊 **Quality Assurance**: 95%+ test coverage, security scanning, automated linting
+
+Includes all **75 Grind problems** - the most essential coding interview questions curated for maximum impact.
+
+## 🎯 What's Included
+
+**Current**: All 75 problems from [Grind 75](https://www.techinterviewhandbook.org/grind75/) - the most essential coding interview problems curated by the creator of Blind 75.
+
+**Future**: Planned expansion to all 169 Grind problems for comprehensive interview preparation.
## 🚀 Quick Start
```bash
-# Clone the repository
+# Clone and setup
git clone https://github.com/wisarootl/leetcode-py.git
cd leetcode-py
-
-# Install dependencies
poetry install
-# Generate all problems to start practicing (fresh start - wipes all solutions)
-make gen-all-problems
+# Start with existing Grind 75 problems
+make gen-all-problems # Regenerates all problems with TODO placeholders
-# Run existing problems
-make p-test PROBLEM=insert_interval
-make p-test PROBLEM=invert_binary_tree
+# Practice a specific problem
+make p-test PROBLEM=two_sum
+# Edit leetcode/two_sum/solution.py, then rerun tests
# Run all tests
make test
@@ -33,113 +47,148 @@ make test
## 📋 Prerequisites
- Python 3.13+
-- make, git, Graphviz, poetry
+- Poetry, Make, Git, Graphviz
## 📁 Problem Structure
-Each problem follows a consistent template:
+Each problem follows a consistent, production-ready template:
```
leetcode/two_sum/
-├── README.md # Problem description and examples
-├── solution.py # Your implementation with TODO placeholder
-├── tests.py # Comprehensive test cases
-├── notebook.ipynb # Interactive playground
-└── __init__.py # Package marker
+├── README.md # Problem description with examples and constraints
+├── solution.py # Implementation with type hints and TODO placeholder
+├── test_solution.py # Comprehensive parametrized tests (12+ test cases)
+├── helpers.py # Test helper functions
+├── playground.py # Interactive debugging environment (converted from .ipynb)
+└── __init__.py # Package marker
```
-## 🎯 Supported Problem Categories (ongoing)
+## ✨ Key Features
-- **Arrays & Hashing** - Two Sum, Group Anagrams, Top K Elements
-- **Two Pointers** - Valid Palindrome, Container With Most Water
-- **Sliding Window** - Longest Substring, Minimum Window
-- **Stack** - Valid Parentheses, Daily Temperatures
-- **Binary Search** - Search Rotated Array, Find Minimum
-- **Linked Lists** - Reverse List, Merge Lists, Detect Cycle
-- **Trees** - Invert Tree, Maximum Depth, Serialize/Deserialize
-- **Tries** - Implement Trie, Word Search II
-- **Heap/Priority Queue** - Merge K Lists, Find Median
-- **Backtracking** - Combination Sum, Word Search, N-Queens
-- **Graphs** - Clone Graph, Course Schedule, Islands
-- **Advanced DP** - Climbing Stairs, Coin Change, LCS
-- **Greedy** - Jump Game, Gas Station
-- **Intervals** - Merge Intervals, Meeting Rooms
-- **Math & Geometry** - Rotate Image, Spiral Matrix
+### Production-Grade Development Environment
-Includes problems from **Blind 75**, **Grind 75**, **NeetCode 150**, and **Top Interview Questions**.
+- **Modern Python**: PEP 585/604 type hints, snake_case conventions
+- **Comprehensive Linting**: black, isort, ruff, mypy with nbqa for notebooks
+- **High Test Coverage**: 12+ test cases per problem including edge cases
+- **Beautiful Logging**: loguru integration for enhanced test debugging
+- **CI/CD Pipeline**: Automated testing, security scanning, and quality gates
-## 🎨 Visualizations
+### Enhanced Data Structure Visualization
-### Tree Visualization
+- **TreeNode**: Beautiful tree rendering with anytree and Graphviz
+- **ListNode**: Clean arrow-based visualization (`1 -> 2 -> 3`)
+- **Interactive Debugging**: Multi-cell playground environment

_Beautiful tree rendering with anytree and Graphviz_
-### Linked List Visualization
-

_Clean arrow-based list visualization_
-### Jupyter Notebook Integration
+### Flexible Notebook Support
+
+- **Template Generation**: Creates Jupyter notebooks (`.ipynb`) by default
+- **Repository State**: This repo uses Python files (`.py`) for better version control
+- **User Choice**: Use `make nb-to-py` to convert notebooks to Python files, or keep as `.ipynb` for interactive development

_Interactive multi-cell playground for each problem_
-## ✨ Features
-
-- **Template-driven development** - Consistent structure for every problem
-- **Beautiful visualizations** - TreeNode with anytree/Graphviz, ListNode with arrows
-- **Interactive notebooks** - Multi-cell playground for each problem
-- **One-command testing** - `make p-test PROBLEM=problem_name`
-- **Bulk regeneration** - `make gen-all-problems` from JSON templates
-- **Full linting** - black, isort, ruff, mypy with nbqa for notebooks
-- **Modern Python** - PEP 585/604 syntax with full type hints
+## 🔄 Usage Patterns
-## 🔄 Workflow Examples
+### For Current Grind 75 Problems
-**Practice existing problems**:
+Perfect if you want to focus on the most essential interview problems:
```bash
-# Work on a specific problem
-make p-test PROBLEM=lru_cache
-# Edit leetcode/lru_cache/solution.py
-# Run tests to verify
+# Regenerate all 75 problems with fresh TODO placeholders
+make gen-all-problems
-# Or use make p-test if default problem is set in Makefile
-make p-test
+# Work through problems systematically
+make p-test PROBLEM=two_sum
+make p-test PROBLEM=valid_palindrome
+make p-test PROBLEM=merge_two_sorted_lists
```
-**Add new problems**:
+### For Additional Problems (LLM-Assisted)
+
+If you need more problems beyond Grind 75, use an LLM assistant in your IDE (Cursor, GitHub Copilot Chat, Amazon Q, etc.):
```bash
-# Ask your LLM assistant:
-# "Create LeetCode problem 146 (LRU Cache)"
-# The assistant handles everything automatically!
+# Example commands to give your LLM assistant:
+"Create LeetCode problem 146 (LRU Cache)"
+"Add problem 'Word Ladder' by number 127"
+"Generate problem 'Serialize and Deserialize Binary Tree'"
+
+# For test enhancement (when you need more comprehensive test coverage):
+"Enhance test cases for two_sum problem"
+"Fix test reproducibility for binary_tree_inorder_traversal"
```
-_Behind the scenes: Assistant follows `.amazonq/rules/problem-creation.md` to scrape problem data, create JSON template, generate structure with `make p-gen`, and verify with `make lint`._
+**Required LLM Context**: Include these rule files in your LLM context for automated problem generation and test enhancement:
+
+- [`.amazonq/rules/problem-creation.md`](.amazonq/rules/problem-creation.md) - Complete problem generation workflow
+- [`.amazonq/rules/test-case-enhancement.md`](.amazonq/rules/test-case-enhancement.md) - Test enhancement and reproducibility verification
-**Bulk operations**:
+**Manual Check**: Find problems needing more test cases:
```bash
-# Test all problems
-make test
-# Regenerate all from templates
-make gen-all-problems
-# Check code quality
-make lint
+poetry run python .templates/check_test_cases.py --threshold=10
```
-## 🧰 Helper Classes (ongoing)
+## 🧰 Helper Classes
- **TreeNode**: `from leetcode_py import TreeNode`
- - Beautiful tree visualization with anytree rendering
- - Jupyter notebook support with Graphviz diagrams
- - Easy array ↔ tree conversion for testing
+ - Array ↔ tree conversion: `TreeNode.from_list([1,2,3])`, `tree.to_list()`
+ - Beautiful anytree text rendering and Graphviz SVG for Jupyter
+ - Node search: `tree.find_node(value)`
+ - Generic type support: `TreeNode[int]`, `TreeNode[str]`
+
- **ListNode**: `from leetcode_py import ListNode`
- - Clean arrow visualization (`1 -> 2 -> 3`)
- - Simple array ↔ list conversion
- - Perfect for debugging linked list problems
-- New helpers: Add to `leetcode_py/`
+ - Array ↔ list conversion: `ListNode.from_list([1,2,3])`, `node.to_list()`
+ - Cycle detection with Floyd's algorithm
+ - Graphviz visualization for Jupyter notebooks
+ - Generic type support: `ListNode[int]`, `ListNode[str]`
+
+- **GraphNode**: `from leetcode_py import GraphNode`
+ - Adjacency list conversion: `GraphNode.from_adjacency_list([[2,4],[1,3],[2,4],[1,3]])`
+ - Clone detection: `original.is_clone(cloned)`
+ - Graphviz visualization for undirected graphs
+ - DFS traversal utilities
+
+- **DictTree**: `from leetcode_py.data_structures import DictTree`
+ - Perfect for Trie implementations: `DictTree[str]()`
+ - Beautiful tree rendering with box-drawing characters
+ - Graphviz visualization for Jupyter notebooks
+ - Generic key type support
+
+## 🛠️ Development Commands
+
+```bash
+# Problem-specific operations
+make p-test PROBLEM=problem_name # Test specific problem
+make p-gen PROBLEM=problem_name # Generate problem from JSON template
+make p-lint PROBLEM=problem_name # Lint specific problem
+
+# Bulk operations
+make test # Run all tests
+make lint # Lint entire codebase
+make gen-all-problems # Regenerate all problems (destructive)
+```
+
+## 🏗️ Architecture
+
+- **Template-Driven**: JSON templates in `.templates/leetcode/json/` drive code generation
+- **Cookiecutter Integration**: Uses `.templates/leetcode/{{cookiecutter.problem_name}}/` template for consistent file structure
+- **Automated Scraping**: LLM-assisted problem data extraction from LeetCode
+- **Version Control Friendly**: Python files by default, optional notebook support
+
+## 📊 Quality Metrics
+
+- **Test Coverage**: 95%+ with comprehensive edge case testing (Codecov integration)
+- **Security**: SonarCloud quality gates, Trivy dependency scanning, Gitleaks secret detection
+- **Code Quality**: Automated linting with black, isort, ruff, mypy
+- **Test Reproducibility**: Automated verification that problems can be regenerated consistently
+- **CI/CD**: GitHub Actions for testing, security, pre-commit hooks, and release automation
-Perfect for interview preparation with professional-grade tooling and beautiful visualizations.
+Perfect for systematic coding interview preparation with professional development practices and enhanced debugging capabilities.
diff --git a/docs/images/linkedlist-viz.png b/docs/images/linkedlist-viz.png
index 46df3b5c81fdde319d5b931eca5706d32505d85f..49c8ed0c4ccd053e2fa8ac7b4f69a00c68bb2ebb 100644
GIT binary patch
literal 39106
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
literal 30293
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