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
17 changes: 17 additions & 0 deletions .cursor/.dev/update_tags.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"neetcode-150": [
"binary_tree_maximum_path_sum",
"find_minimum_in_rotated_sorted_array",
"number_of_1_bits",
"reorder_list",
"reverse_bits"
],
"algo-master-75": ["binary_tree_maximum_path_sum"],
"blind-75": [
"binary_tree_maximum_path_sum",
"find_minimum_in_rotated_sorted_array",
"number_of_1_bits",
"reorder_list",
"reverse_bits"
]
}
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PYTHON_VERSION = 3.13
PROBLEM ?= same_tree
PROBLEM ?= number_of_1_bits
FORCE ?= 0
COMMA := ,

Expand Down
42 changes: 42 additions & 0 deletions leetcode/binary_tree_maximum_path_sum/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Binary Tree Maximum Path Sum

**Difficulty:** Hard
**Topics:** Dynamic Programming, Tree, Depth-First Search, Binary Tree
**Tags:** blind-75

**LeetCode:** [Problem 124](https://leetcode.com/problems/binary-tree-maximum-path-sum/description/)

## Problem Description

A **path** in a binary tree is a sequence of nodes where each pair of adjacent nodes in the sequence has an edge connecting them. A node can only appear in the sequence **at most once**. Note that the path does not need to pass through the root.

The **path sum** of a path is the sum of the node's values in the path.

Given the `root` of a binary tree, return _the maximum **path sum** of any **non-empty** path_.

## Examples

### Example 1:

![Example 1](https://assets.leetcode.com/uploads/2020/10/13/exx1.jpg)

```
Input: root = [1,2,3]
Output: 6
Explanation: The optimal path is 2 -> 1 -> 3 with a path sum of 2 + 1 + 3 = 6.
```

### Example 2:

![Example 2](https://assets.leetcode.com/uploads/2020/10/13/exx2.jpg)

```
Input: root = [-10,9,20,null,null,15,7]
Output: 42
Explanation: The optimal path is 15 -> 20 -> 7 with a path sum of 15 + 20 + 7 = 42.
```

## Constraints

- The number of nodes in the tree is in the range [1, 3 * 10^4].
- -1000 <= Node.val <= 1000
Empty file.
12 changes: 12 additions & 0 deletions leetcode/binary_tree_maximum_path_sum/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from leetcode_py import TreeNode


def run_max_path_sum(solution_class: type, root_list: list[int | None]):
root = TreeNode[int].from_list(root_list)
implementation = solution_class()
return implementation.max_path_sum(root)


def assert_max_path_sum(result: int, expected: int) -> bool:
assert result == expected
return True
29 changes: 29 additions & 0 deletions leetcode/binary_tree_maximum_path_sum/playground.py
Original file line number Diff line number Diff line change
@@ -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_max_path_sum, run_max_path_sum
from solution import Solution

# %%
# Example test case
root_list: list[int | None] = [1, 2, 3]
expected: int = 6

# %%
result = run_max_path_sum(Solution, root_list)
result

# %%
assert_max_path_sum(result, expected)
48 changes: 48 additions & 0 deletions leetcode/binary_tree_maximum_path_sum/solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from leetcode_py import TreeNode


class Solution:

# Time: O(n) where n is the number of nodes
# Space: O(h) where h is the height of the tree (recursion stack)
def max_path_sum(self, root: TreeNode[int] | None) -> int:
"""
Find the maximum path sum in a binary tree.

A path is a sequence of nodes where each pair of adjacent nodes
has an edge connecting them. A node can only appear once in the path.
The path doesn't need to pass through the root.

Uses DFS with post-order traversal to calculate:
1. Maximum path sum that can be extended upward from current node
2. Maximum path sum that includes current node as the highest point
"""
if not root:
return 0

max_sum = float("-inf")

def dfs(node: TreeNode[int] | None) -> int:
nonlocal max_sum

if not node:
return 0

# Get maximum path sum from left and right subtrees
# If negative, we don't include them (take 0 instead)
left_max = max(0, dfs(node.left))
right_max = max(0, dfs(node.right))

# Current path sum if this node is the highest point
# (left path + current node + right path)
current_path_sum = node.val + left_max + right_max

# Update global maximum
max_sum = max(max_sum, current_path_sum)

# Return maximum path sum that can be extended upward
# (either left or right path + current node)
return node.val + max(left_max, right_max)

dfs(root)
return int(max_sum)
36 changes: 36 additions & 0 deletions leetcode/binary_tree_maximum_path_sum/test_solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import pytest

from leetcode_py import logged_test

from .helpers import assert_max_path_sum, run_max_path_sum
from .solution import Solution


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

@logged_test
@pytest.mark.parametrize(
"root_list, expected",
[
([1, 2, 3], 6),
([-10, 9, 20, None, None, 15, 7], 42),
([1], 1),
([-3], -3),
([1, -2, 3], 4),
([5, 4, 8, 11, None, 13, 4, 7, 2, None, None, None, 1], 48),
([1, 2, 3, 4, 5], 11),
([-1, -2, -3], -1),
([1, -1, 2], 3),
([1, 2, -3, 4, 5], 11),
([1, 2, 3, None, None, 4, 5], 12),
([-1, 2, 3], 4),
([1, -2, -3, 1, 3, -2, None, -1], 3),
([2, -1], 2),
([1, 2], 3),
],
)
def test_max_path_sum(self, root_list: list[int | None], expected: int):
result = run_max_path_sum(Solution, root_list)
assert_max_path_sum(result, expected)
54 changes: 54 additions & 0 deletions leetcode/find_minimum_in_rotated_sorted_array/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Find Minimum in Rotated Sorted Array

**Difficulty:** Medium
**Topics:** Array, Binary Search
**Tags:** blind-75

**LeetCode:** [Problem 153](https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/description/)

## Problem Description

Suppose an array of length `n` sorted in ascending order is **rotated** between `1` and `n` times. For example, the array `nums = [0,1,2,4,5,6,7]` might become:

- `[4,5,6,7,0,1,2]` if it was rotated `4` times.
- `[0,1,2,4,5,6,7]` if it was rotated `7` times.

Notice that **rotating** an array `[a[0], a[1], a[2], ..., a[n-1]]` 1 time results in the array `[a[n-1], a[0], a[1], a[2], ..., a[n-2]]`.

Given the sorted rotated array `nums` of **unique** elements, return _the minimum element of this array_.

You must write an algorithm that runs in O(log n) time.

## Examples

### Example 1:

```
Input: nums = [3,4,5,1,2]
Output: 1
Explanation: The original array was [1,2,3,4,5] rotated 3 times.
```

### Example 2:

```
Input: nums = [4,5,6,7,0,1,2]
Output: 0
Explanation: The original array was [0,1,2,4,5,6,7] and it was rotated 4 times.
```

### Example 3:

```
Input: nums = [11,13,15,17]
Output: 11
Explanation: The original array was [11,13,15,17] and it was rotated 4 times.
```

## Constraints

- n == nums.length
- 1 <= n <= 5000
- -5000 <= nums[i] <= 5000
- All the integers of nums are **unique**.
- nums is sorted and rotated between 1 and n times.
Empty file.
8 changes: 8 additions & 0 deletions leetcode/find_minimum_in_rotated_sorted_array/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
def run_find_min(solution_class: type, nums: list[int]):
implementation = solution_class()
return implementation.find_min(nums)


def assert_find_min(result: int, expected: int) -> bool:
assert result == expected
return True
29 changes: 29 additions & 0 deletions leetcode/find_minimum_in_rotated_sorted_array/playground.py
Original file line number Diff line number Diff line change
@@ -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_min, run_find_min
from solution import Solution

# %%
# Example test case
nums: list[int] = [3, 4, 5, 1, 2]
expected: int = 1

# %%
result = run_find_min(Solution, nums)
result

# %%
assert_find_min(result, expected)
37 changes: 37 additions & 0 deletions leetcode/find_minimum_in_rotated_sorted_array/solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
class Solution:

# Time: O(log n) - binary search
# Space: O(1) - only using constant extra space
def find_min(self, nums: list[int]) -> int:
"""
Find the minimum element in a rotated sorted array using binary search.

The key insight is that in a rotated sorted array, one half is always sorted.
We can determine which half contains the minimum by comparing the middle
element with the rightmost element.

Algorithm:
1. If nums[left] < nums[right], the array is not rotated, return nums[left]
2. Otherwise, find the rotation point using binary search
3. The minimum is always at the rotation point
"""
left, right = 0, len(nums) - 1

# If the array is not rotated, the first element is the minimum
if nums[left] < nums[right]:
return nums[left]

# Binary search to find the rotation point
while left < right:
mid = left + (right - left) // 2

# If mid element is greater than right element,
# the rotation point is in the right half
if nums[mid] > nums[right]:
left = mid + 1
else:
# If mid element is less than or equal to right element,
# the rotation point is in the left half (including mid)
right = mid

return nums[left]
39 changes: 39 additions & 0 deletions leetcode/find_minimum_in_rotated_sorted_array/test_solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import pytest

from leetcode_py import logged_test

from .helpers import assert_find_min, run_find_min
from .solution import Solution


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

@logged_test
@pytest.mark.parametrize(
"nums, expected",
[
([3, 4, 5, 1, 2], 1),
([4, 5, 6, 7, 0, 1, 2], 0),
([11, 13, 15, 17], 11),
([1], 1),
([2, 1], 1),
([1, 2, 3], 1),
([3, 1, 2], 1),
([2, 3, 1], 1),
([4, 5, 6, 7, 8, 1, 2, 3], 1),
([5, 6, 7, 8, 9, 10, 1, 2, 3, 4], 1),
([6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5], 1),
([7, 8, 9, 10, 11, 12, 13, 14, 1, 2, 3, 4, 5, 6], 1),
([8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7], 1),
([9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 1, 2, 3, 4, 5, 6, 7, 8], 1),
(
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9],
1,
),
],
)
def test_find_min(self, nums: list[int], expected: int):
result = run_find_min(Solution, nums)
assert_find_min(result, expected)
Loading