Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .amazonq/rules/development-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

- Use snake_case for Python method names (following Python convention)
- Always include type hints for function parameters and return types
- Use PEP 585/604 syntax: `list[str]`, `dict[str, int]`, `Type | None`, etc.
- Add return statements to satisfy type checkers even if unreachable
- Follow the project's linting rules (black, isort, ruff, mypy)

Expand All @@ -26,6 +27,13 @@
- `{parameters}` - method parameters with types
- `{return_type}` - return type annotation
- Test case placeholders with actual examples
- **Template Implementation**: Do NOT implement the Solution class - only provide test cases and structure
- **Helper Functions/Classes**: If the question relies on underlying helper functions or classes (e.g., TreeNode, ListNode):
- First check if implementation already exists in `leetcode_py/common/` directory
- If found, import from common module
- If not found, create shared implementation in `leetcode_py/common/` for reusable classes
- For question-specific helpers, implement directly in the solution file
- **Update Makefile**: When adding new question, update the default `QUESTION` value in Makefile to the new question name
- Always use the template structure for consistency

## File Structure
Expand Down
8 changes: 8 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
coverage:
status:
project:
default:
target: 70%
patch:
default:
target: 70%
10 changes: 5 additions & 5 deletions .github/workflows/ci-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,30 @@ on:
push:
branches: [main]
pull_request:
branches: [main]
types: [opened, synchronize, reopened]

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.13"

- name: Install Poetry
uses: snok/install-poetry@v1
uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1.4.1
with:
virtualenvs-create: true
virtualenvs-in-project: true
installer-parallel: true

- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v4
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: .venv
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
Expand Down
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
PYTHON_VERSION = 3.13
QUESTION ?= two_sum
QUESTION ?= invert_binary_tree

sync_submodules:
git submodule update --init --recursive --remote

# WARNING: Opinionated development setup for macOS + zsh + brew
# This will install/update many tools: pipx, poetry, pre-commit, etc.
# Only use if you understand what it does and accept the changes
setup_dev: sync_submodules
chmod +x scripts/shared/python/poetry/setup_dev.sh
./scripts/shared/python/poetry/setup_dev.sh \
Expand Down Expand Up @@ -42,4 +45,4 @@ test-question:
echo "Error: Question '$(QUESTION)' not found in leetcode/ directory"; \
exit 1; \
fi
cd leetcode/$(QUESTION) && poetry run pytest tests.py -v -s
poetry run pytest leetcode/$(QUESTION)/tests.py -v -s
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# LeetCode Practice Repository 🚀

[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=wisarootl_leetcode-py&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=wisarootl_leetcode-py)
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=wisarootl_leetcode-py&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=wisarootl_leetcode-py)
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=wisarootl_leetcode-py&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=wisarootl_leetcode-py)
[![codecov](https://codecov.io/gh/wisarootl/leetcode-py/graph/badge.svg?token=TI97VUIA4Z)](https://codecov.io/gh/wisarootl/leetcode-py)
[![tests](https://img.shields.io/github/actions/workflow/status/wisarootl/leetcode-py/ci-test.yml?branch=main&label=tests&logo=github)](https://github.com/wisarootl/zerv/actions/workflows/ci-test.yml)
[![release](https://img.shields.io/github/actions/workflow/status/wisarootl/leetcode-py/cd.yml?branch=main&label=release&logo=github)](https://github.com/wisarootl/zerv/actions/workflows/cd.yml)

Premium LeetCode practice environment with modern Python tooling, beautiful tree visualizations, and comprehensive testing.

## ✨ Features

- **Template-driven development** - Consistent structure for every problem
- **Beautiful tree visualizations** - Pretty-printed binary trees with anytree
- **Rich test logging** - `@logged_test` decorator with detailed tracebacks
- **One-command testing** - `make test-question QUESTION=problem_name`
- **Code quality** - black, isort, ruff, mypy integration
- **Modern Python** - PEP 585/604 syntax with full type hints

## 🚀 Quick Start

```bash
# Run existing problems
make test-question QUESTION=two_sum
make test-question QUESTION=invert_binary_tree

# Run all tests
make test
```

**Adding new problems**: Use an LLM agent (rules in `.amazonq/rules/development-rules.md`) to automatically create new problems from copied LeetCode problem text using the template structure.

## 🧰 Commands

```bash
make test-question QUESTION=two_sum # Test specific problem
make test # Run all tests
make lint # Code quality checks
```

## 🎨 Example Output

```
# TreeNode visualization
4
├── 2
│ ├── 1
│ └── 3
└── 7
├── 6
└── 9

# Test logging
2024-01-01 10:00:00 | SUCCESS | Got result: [4,7,2,9,6,3,1]
2024-01-01 10:00:00 | DEBUG | Test passed! ✨
```

Perfect for interview preparation with professional-grade tooling and beautiful visualizations.
9 changes: 1 addition & 8 deletions leetcode/_template/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

**Difficulty:** {DIFFICULTY}
**Topics:** {TOPICS}
**Tags:** {TAGS}

## Problem Description

Expand Down Expand Up @@ -39,13 +40,5 @@ Output: {OUTPUT_3}

{FOLLOW_UP}

## Solution

```python
def {method_name}({parameters}):
# TODO: Implement solution
pass
```

**Time Complexity:** O(?)
**Space Complexity:** O(?)
2 changes: 2 additions & 0 deletions leetcode/_template/solution.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@


class Solution:
# Time: O(?)
# Space: O(?)
def {method_name}(self, {parameters}) -> {return_type}:
# TODO: Implement solution
pass
3 changes: 2 additions & 1 deletion leetcode/_template/tests.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest
from loguru import logger
from leetcode_py.test_utils import logged_test
from .solution import Solution


Expand All @@ -15,9 +16,9 @@ def setup_method(self):
({test_case_3}),
],
)
@logged_test
def test_{method_name}(self, {param_names}):
logger.info(f"Testing with {input_description}")
result = self.solution.{method_name}({input_params})
logger.success(f"Got result: {result}")
assert result == {expected_param}
logger.debug("Test passed! ✨")
40 changes: 40 additions & 0 deletions leetcode/invert_binary_tree/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# 226. Invert Binary Tree

**Difficulty:** Easy
**Topics:** Tree, Depth-First Search, Breadth-First Search, Binary Tree
**Tags:** grind-75

## Problem Description

Given the root of a binary tree, invert the tree, and return its root.

## Examples

### Example 1:

```
Input: root = [4,2,7,1,3,6,9]
Output: [4,7,2,9,6,3,1]
```

### Example 2:

```
Input: root = [2,1,3]
Output: [2,3,1]
```

### Example 3:

```
Input: root = []
Output: []
```

## Constraints

- The number of nodes in the tree is in the range [0, 100].
- -100 <= Node.val <= 100

**Time Complexity:** O(?)
**Space Complexity:** O(?)
Empty file.
12 changes: 12 additions & 0 deletions leetcode/invert_binary_tree/solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from leetcode_py.tree_node import TreeNode


class Solution:
# Time: O(n) - visit each node once
# Space: O(h) - recursion stack depth equals tree height
def invert_tree(self, root: TreeNode | None) -> TreeNode | None:
if not root:
return root

root.left, root.right = self.invert_tree(root.right), self.invert_tree(root.left)
return root
40 changes: 40 additions & 0 deletions leetcode/invert_binary_tree/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import pytest
from loguru import logger

from leetcode_py.test_utils import logged_test
from leetcode_py.tree_node import TreeNode

from .solution import Solution


class TestInvertBinaryTree:
def setup_method(self):
self.solution = Solution()

@pytest.mark.parametrize(
"input_arr, expected_arr",
[
([4, 2, 7, 1, 3, 6, 9], [4, 7, 2, 9, 6, 3, 1]),
([2, 1, 3], [2, 3, 1]),
([], []),
([1], [1]),
([1, 2], [1, None, 2]),
([1, None, 2], [1, 2]),
([1, 2, 3, 4, 5], [1, 3, 2, None, None, 5, 4]),
([3, 9, 20, None, None, 15, 7], [3, 20, 9, 7, 15]),
],
)
@logged_test
def test_invert_tree(self, input_arr: list[int | None], expected_arr: list[int | None]):
logger.info(f"Testing with input: {input_arr}")
root = TreeNode.from_list(input_arr)
if root:
logger.debug(f"Input tree:\n{root}")

result = self.solution.invert_tree(root)
result_arr = result.to_list() if result else []

if result:
logger.debug(f"Result tree:\n{result}")
logger.success(f"Got result: {result_arr}")
assert result_arr == expected_arr
13 changes: 1 addition & 12 deletions leetcode/two_sum/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

**Difficulty:** Easy
**Topics:** Array, Hash Table
**Tags:** grind-75

## Problem Description

Expand Down Expand Up @@ -46,17 +47,5 @@ Output: [0,1]

Can you come up with an algorithm that is less than O(n²) time complexity?

## Solution

```python
def twoSum(nums, target):
seen = {}
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
```

**Time Complexity:** O(n)
**Space Complexity:** O(n)
2 changes: 2 additions & 0 deletions leetcode/two_sum/solution.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@


class Solution:
# Time: O(n) - single pass through array
# Space: O(n) - hash map stores up to n elements
def two_sum(self, nums: List[int], target: int) -> List[int]:
seen: dict[int, int] = {}
for i, num in enumerate(nums):
Expand Down
6 changes: 5 additions & 1 deletion leetcode/two_sum/tests.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import pytest
from loguru import logger

from leetcode_py.test_utils import logged_test

from .solution import Solution


Expand All @@ -16,7 +18,9 @@ def setup_method(self):
([3, 3], 6, [0, 1]),
],
)
@logged_test
def test_two_sum(self, nums, target, expected):
logger.info(f"Testing with nums: {nums}, target: {target}")
result = self.solution.two_sum(nums, target)
logger.success(f"Got result: {result}")
assert result == expected
logger.success("test")
20 changes: 20 additions & 0 deletions leetcode_py/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from functools import wraps

from loguru import logger


def logged_test(func):
"""Decorator to add consistent logging to test methods."""

@wraps(func)
def wrapper(*args, **kwargs):
logger.info("")
try:
result = func(*args, **kwargs)
logger.debug("Test passed! ✨")
return result
except Exception as e:
logger.exception(f"Test failed: {e}")
raise

return wrapper
Loading