From 614ad1a951128685f95124e6cf9d620072ec0e35 Mon Sep 17 00:00:00 2001 From: Wisaroot Lertthaweedech Date: Thu, 4 Sep 2025 20:47:07 +0700 Subject: [PATCH 1/6] feat: add Merge Intervals --- .templates/leetcode/json/merge_intervals.json | 50 ++++++++++++++ Makefile | 2 +- leetcode/merge_intervals/README.md | 46 +++++++++++++ leetcode/merge_intervals/__init__.py | 0 leetcode/merge_intervals/playground.ipynb | 67 +++++++++++++++++++ leetcode/merge_intervals/solution.py | 14 ++++ leetcode/merge_intervals/tests.py | 32 +++++++++ 7 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 .templates/leetcode/json/merge_intervals.json create mode 100644 leetcode/merge_intervals/README.md create mode 100644 leetcode/merge_intervals/__init__.py create mode 100644 leetcode/merge_intervals/playground.ipynb create mode 100644 leetcode/merge_intervals/solution.py create mode 100644 leetcode/merge_intervals/tests.py diff --git a/.templates/leetcode/json/merge_intervals.json b/.templates/leetcode/json/merge_intervals.json new file mode 100644 index 0000000..071e1bf --- /dev/null +++ b/.templates/leetcode/json/merge_intervals.json @@ -0,0 +1,50 @@ +{ + "problem_name": "merge_intervals", + "solution_class_name": "Solution", + "problem_number": "56", + "problem_title": "Merge Intervals", + "difficulty": "Medium", + "topics": "Array, Sorting", + "tags": ["grind-75"], + "readme_description": "Given an array of `intervals` where `intervals[i] = [starti, endi]`, merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input.", + "readme_examples": [ + { + "content": "```\nInput: intervals = [[1,3],[2,6],[8,10],[15,18]]\nOutput: [[1,6],[8,10],[15,18]]\n```\n**Explanation:** Since intervals [1,3] and [2,6] overlap, merge them into [1,6]." + }, + { + "content": "```\nInput: intervals = [[1,4],[4,5]]\nOutput: [[1,5]]\n```\n**Explanation:** Intervals [1,4] and [4,5] are considered overlapping." + }, + { + "content": "```\nInput: intervals = [[4,7],[1,4]]\nOutput: [[1,7]]\n```\n**Explanation:** Intervals [1,4] and [4,7] are considered overlapping." + } + ], + "readme_constraints": "- `1 <= intervals.length <= 10^4`\n- `intervals[i].length == 2`\n- `0 <= starti <= endi <= 10^4`", + "readme_additional": "", + "solution_imports": "", + "solution_methods": [ + { + "name": "merge", + "parameters": "intervals: list[list[int]]", + "return_type": "list[list[int]]", + "dummy_return": "[]" + } + ], + "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution", + "test_class_name": "MergeIntervals", + "test_helper_methods": [ + { "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" } + ], + "test_methods": [ + { + "name": "test_merge", + "parametrize": "intervals, expected", + "parametrize_typed": "intervals: list[list[int]], expected: list[list[int]]", + "test_cases": "[([[1,3],[2,6],[8,10],[15,18]], [[1,6],[8,10],[15,18]]), ([[1,4],[4,5]], [[1,5]]), ([[4,7],[1,4]], [[1,7]])]", + "body": "result = self.solution.merge(intervals)\nassert result == expected" + } + ], + "playground_imports": "from solution import Solution", + "playground_test_case": "# Example test case\nintervals = [[1,3],[2,6],[8,10],[15,18]]\nexpected = [[1,6],[8,10],[15,18]]", + "playground_execution": "result = Solution().merge(intervals)\nresult", + "playground_assertion": "assert result == expected" +} diff --git a/Makefile b/Makefile index 399169f..2d52981 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PYTHON_VERSION = 3.13 -PROBLEM ?= longest_palindrome +PROBLEM ?= merge_intervals FORCE ?= 0 COMMA := , diff --git a/leetcode/merge_intervals/README.md b/leetcode/merge_intervals/README.md new file mode 100644 index 0000000..0f648cf --- /dev/null +++ b/leetcode/merge_intervals/README.md @@ -0,0 +1,46 @@ +# Merge Intervals + +**Difficulty:** Medium +**Topics:** Array, Sorting +**Tags:** grind-75 + +**LeetCode:** [Problem 56](https://leetcode.com/problems/merge-intervals/description/) + +## Problem Description + +Given an array of `intervals` where `intervals[i] = [starti, endi]`, merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input. + +## Examples + +### Example 1: + +``` +Input: intervals = [[1,3],[2,6],[8,10],[15,18]] +Output: [[1,6],[8,10],[15,18]] +``` + +**Explanation:** Since intervals [1,3] and [2,6] overlap, merge them into [1,6]. + +### Example 2: + +``` +Input: intervals = [[1,4],[4,5]] +Output: [[1,5]] +``` + +**Explanation:** Intervals [1,4] and [4,5] are considered overlapping. + +### Example 3: + +``` +Input: intervals = [[4,7],[1,4]] +Output: [[1,7]] +``` + +**Explanation:** Intervals [1,4] and [4,7] are considered overlapping. + +## Constraints + +- `1 <= intervals.length <= 10^4` +- `intervals[i].length == 2` +- `0 <= starti <= endi <= 10^4` diff --git a/leetcode/merge_intervals/__init__.py b/leetcode/merge_intervals/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/leetcode/merge_intervals/playground.ipynb b/leetcode/merge_intervals/playground.ipynb new file mode 100644 index 0000000..ae09a51 --- /dev/null +++ b/leetcode/merge_intervals/playground.ipynb @@ -0,0 +1,67 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "imports", + "metadata": {}, + "outputs": [], + "source": [ + "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": "execute", + "metadata": {}, + "outputs": [], + "source": [ + "result = Solution().merge(intervals)\nresult" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "test", + "metadata": {}, + "outputs": [], + "source": [ + "assert 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", + "pygments_lexer": "ipython3", + "version": "3.13.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/leetcode/merge_intervals/solution.py b/leetcode/merge_intervals/solution.py new file mode 100644 index 0000000..55ca082 --- /dev/null +++ b/leetcode/merge_intervals/solution.py @@ -0,0 +1,14 @@ +class Solution: + # Time: O(n log n) + # Space: O(1) + def merge(self, intervals: list[list[int]]) -> list[list[int]]: + intervals.sort() + merged = [intervals[0]] + + for start, end in intervals[1:]: + if start <= merged[-1][1]: + merged[-1][1] = max(merged[-1][1], end) + else: + merged.append([start, end]) + + return merged diff --git a/leetcode/merge_intervals/tests.py b/leetcode/merge_intervals/tests.py new file mode 100644 index 0000000..812653e --- /dev/null +++ b/leetcode/merge_intervals/tests.py @@ -0,0 +1,32 @@ +import pytest + +from leetcode_py.test_utils import logged_test + +from .solution import Solution + + +class TestMergeIntervals: + def setup_method(self): + self.solution = Solution() + + @pytest.mark.parametrize( + "intervals, expected", + [ + # Original test cases + ([[1, 3], [2, 6], [8, 10], [15, 18]], [[1, 6], [8, 10], [15, 18]]), + ([[1, 4], [4, 5]], [[1, 5]]), + ([[4, 7], [1, 4]], [[1, 7]]), + # Edge cases + ([[1, 1]], [[1, 1]]), # Single point interval + ([[1, 2], [3, 4]], [[1, 2], [3, 4]]), # No overlap + ([[1, 4], [2, 3]], [[1, 4]]), # Complete overlap + ([[1, 10], [2, 6], [8, 10], [15, 18]], [[1, 10], [15, 18]]), # Multiple merges + ([[0, 0], [1, 1], [2, 2]], [[0, 0], [1, 1], [2, 2]]), # All single points + ([[1, 3], [2, 6], [8, 10], [9, 12], [15, 18]], [[1, 6], [8, 12], [15, 18]]), # Chain merge + ([[4, 5], [4, 6]], [[4, 6]]), # Same start time + ], + ) + @logged_test + def test_merge(self, intervals: list[list[int]], expected: list[list[int]]): + result = self.solution.merge(intervals) + assert result == expected From 350404c7ff2b10cba083c43ee065af4dde78f50a Mon Sep 17 00:00:00 2001 From: Wisaroot Lertthaweedech Date: Thu, 4 Sep 2025 21:05:25 +0700 Subject: [PATCH 2/6] feat: add Reverse Linked List --- .../leetcode/json/reverse_linked_list.json | 48 ++++++++ Makefile | 2 +- leetcode/reverse_linked_list/README.md | 45 +++++++ leetcode/reverse_linked_list/__init__.py | 0 leetcode/reverse_linked_list/playground.ipynb | 114 ++++++++++++++++++ leetcode/reverse_linked_list/solution.py | 46 +++++++ leetcode/reverse_linked_list/tests.py | 44 +++++++ 7 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 .templates/leetcode/json/reverse_linked_list.json create mode 100644 leetcode/reverse_linked_list/README.md create mode 100644 leetcode/reverse_linked_list/__init__.py create mode 100644 leetcode/reverse_linked_list/playground.ipynb create mode 100644 leetcode/reverse_linked_list/solution.py create mode 100644 leetcode/reverse_linked_list/tests.py diff --git a/.templates/leetcode/json/reverse_linked_list.json b/.templates/leetcode/json/reverse_linked_list.json new file mode 100644 index 0000000..2c036c2 --- /dev/null +++ b/.templates/leetcode/json/reverse_linked_list.json @@ -0,0 +1,48 @@ +{ + "problem_name": "reverse_linked_list", + "solution_class_name": "Solution", + "problem_number": "206", + "problem_title": "Reverse Linked List", + "difficulty": "Easy", + "topics": "Linked List, Recursion", + "tags": ["grind-75"], + "readme_description": "Given the `head` of a singly linked list, reverse the list, and return the reversed list.", + "readme_examples": [ + { + "content": "![Example 1](https://assets.leetcode.com/uploads/2021/02/19/rev1ex1.jpg)\n\n```\nInput: head = [1,2,3,4,5]\nOutput: [5,4,3,2,1]\n```" + }, + { + "content": "![Example 2](https://assets.leetcode.com/uploads/2021/02/19/rev1ex2.jpg)\n\n```\nInput: head = [1,2]\nOutput: [2,1]\n```" + }, + { "content": "```\nInput: head = []\nOutput: []\n```" } + ], + "readme_constraints": "- The number of nodes in the list is the range `[0, 5000]`.\n- `-5000 <= Node.val <= 5000`", + "readme_additional": "**Follow up:** A linked list can be reversed either iteratively or recursively. Could you implement both?", + "solution_imports": "from leetcode_py import ListNode", + "solution_methods": [ + { + "name": "reverse_list", + "parameters": "head: ListNode[int] | None", + "return_type": "ListNode[int] | None", + "dummy_return": "None" + } + ], + "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom leetcode_py import ListNode\nfrom .solution import Solution", + "test_class_name": "ReverseLinkedList", + "test_helper_methods": [ + { "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" } + ], + "test_methods": [ + { + "name": "test_reverse_list", + "parametrize": "head_list, expected_list", + "parametrize_typed": "head_list: list[int], expected_list: list[int]", + "test_cases": "[([1, 2, 3, 4, 5], [5, 4, 3, 2, 1]), ([1, 2], [2, 1]), ([1], [1]), ([], []), ([1, 2, 3], [3, 2, 1]), ([1, 2, 3, 4], [4, 3, 2, 1]), ([-1, -2, -3], [-3, -2, -1]), ([0], [0]), ([5000, -5000], [-5000, 5000]), ([1, 1, 1], [1, 1, 1])]", + "body": "head = ListNode.from_list(head_list)\nexpected = ListNode.from_list(expected_list)\nresult = self.solution.reverse_list(head)\nassert result == expected" + } + ], + "playground_imports": "from leetcode_py import ListNode\nfrom solution import Solution", + "playground_test_case": "# Example test case\nhead_list = [1, 2, 3, 4, 5]\nexpected_list = [5, 4, 3, 2, 1]\nhead = ListNode.from_list(head_list)\nexpected = ListNode.from_list(expected_list)", + "playground_execution": "result = Solution().reverse_list(head)\nresult", + "playground_assertion": "assert result == expected" +} diff --git a/Makefile b/Makefile index 2d52981..a94d3bd 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PYTHON_VERSION = 3.13 -PROBLEM ?= merge_intervals +PROBLEM ?= reverse_linked_list FORCE ?= 0 COMMA := , diff --git a/leetcode/reverse_linked_list/README.md b/leetcode/reverse_linked_list/README.md new file mode 100644 index 0000000..ae96ddd --- /dev/null +++ b/leetcode/reverse_linked_list/README.md @@ -0,0 +1,45 @@ +# Reverse Linked List + +**Difficulty:** Easy +**Topics:** Linked List, Recursion +**Tags:** grind-75 + +**LeetCode:** [Problem 206](https://leetcode.com/problems/reverse-linked-list/description/) + +## Problem Description + +Given the `head` of a singly linked list, reverse the list, and return the reversed list. + +## Examples + +### Example 1: + +![Example 1](https://assets.leetcode.com/uploads/2021/02/19/rev1ex1.jpg) + +``` +Input: head = [1,2,3,4,5] +Output: [5,4,3,2,1] +``` + +### Example 2: + +![Example 2](https://assets.leetcode.com/uploads/2021/02/19/rev1ex2.jpg) + +``` +Input: head = [1,2] +Output: [2,1] +``` + +### Example 3: + +``` +Input: head = [] +Output: [] +``` + +## Constraints + +- The number of nodes in the list is the range `[0, 5000]`. +- `-5000 <= Node.val <= 5000` + +**Follow up:** A linked list can be reversed either iteratively or recursively. Could you implement both? diff --git a/leetcode/reverse_linked_list/__init__.py b/leetcode/reverse_linked_list/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/leetcode/reverse_linked_list/playground.ipynb b/leetcode/reverse_linked_list/playground.ipynb new file mode 100644 index 0000000..71f31da --- /dev/null +++ b/leetcode/reverse_linked_list/playground.ipynb @@ -0,0 +1,114 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "imports", + "metadata": {}, + "outputs": [], + "source": [ + "from solution import Solution\n", + "\n", + "from leetcode_py import ListNode" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "setup", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original: [1, 2, 3, 4, 5]\n", + "Expected: [5, 4, 3, 2, 1]\n" + ] + } + ], + "source": [ + "# Example test case: reverse [1,2,3,4,5] to [5,4,3,2,1]\n", + "head_list = [1, 2, 3, 4, 5]\n", + "expected_list = [5, 4, 3, 2, 1]\n", + "\n", + "# Convert lists to linked lists\n", + "head = ListNode.from_list(head_list)\n", + "expected = ListNode.from_list(expected_list)\n", + "print(f\"Original: {head}\")\n", + "print(f\"Expected: {expected}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "execute", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Result: [5, 4, 3, 2, 1]\n" + ] + }, + { + "data": { + "text/plain": [ + "[5, 4, 3, 2, 1]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Reverse the linked list: 1->2->3->4->5 becomes 5->4->3->2->1\n", + "result = Solution().reverse_list(head)\n", + "print(f\"Result: {result}\")\n", + "result" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "test", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✅ Test passed! Linked list successfully reversed.\n" + ] + } + ], + "source": [ + "# Verify the result matches expected output\n", + "assert 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/reverse_linked_list/solution.py b/leetcode/reverse_linked_list/solution.py new file mode 100644 index 0000000..58a0cbe --- /dev/null +++ b/leetcode/reverse_linked_list/solution.py @@ -0,0 +1,46 @@ +from leetcode_py import ListNode + + +class Solution: + # Time: O(n) + # Space: O(1) + def reverse_list(self, head: ListNode[int] | None) -> ListNode[int] | None: + if not head: + return None + + # Iterative approach using three pointers + # Example: [1,2,3] -> [3,2,1] + # + # Initial: prev curr + # None ↓ + # 1 -> 2 -> 3 -> None + # + prev: ListNode[int] | None = None + curr: ListNode[int] | None = head + + while curr: + # Store next node before breaking the link + next_node = curr.next + # + # prev curr next_node + # None ↓ ↓ + # 1 -> 2 -> 3 -> None + # + + # Reverse the current link + curr.next = prev + # None <- 1 2 -> 3 -> None + # prev curr next_node + # + + # Move pointers forward + prev = curr + curr = next_node + # 1 <- 2 3 -> None + # prev curr + # + + # 1 <- 2 <- 3 None + # prev curr + # prev now points to new head of reversed list + return prev diff --git a/leetcode/reverse_linked_list/tests.py b/leetcode/reverse_linked_list/tests.py new file mode 100644 index 0000000..c8f51a9 --- /dev/null +++ b/leetcode/reverse_linked_list/tests.py @@ -0,0 +1,44 @@ +import pytest + +from leetcode_py import ListNode +from leetcode_py.test_utils import logged_test + +from .solution import Solution + + +class TestReverseLinkedList: + def setup_method(self): + self.solution = Solution() + + @pytest.mark.parametrize( + "head_list, expected_list", + [ + ([1, 2, 3, 4, 5], [5, 4, 3, 2, 1]), # Basic case + ([1, 2], [2, 1]), # Two nodes + ([1], [1]), # Single node + ([], []), # Empty list + ([1, 2, 3], [3, 2, 1]), # Odd length + ([1, 2, 3, 4], [4, 3, 2, 1]), # Even length + ([-1, -2, -3], [-3, -2, -1]), # Negative values + ([0], [0]), # Zero value + ([5000, -5000], [-5000, 5000]), # Boundary values + ([1, 1, 1], [1, 1, 1]), # Duplicate values + ], + ) + @logged_test + def test_reverse_list(self, head_list: list[int], expected_list: list[int]): + # Convert input list to linked list structure + # e.g., [1,2,3] -> 1->2->3->None + head = ListNode.from_list(head_list) + + # Convert expected result list to linked list for comparison + # e.g., [3,2,1] -> 3->2->1->None + expected = ListNode.from_list(expected_list) + + # Call the reverse_list method to reverse the linked list + # This should transform 1->2->3->None into 3->2->1->None + result = self.solution.reverse_list(head) + + # Verify that the reversed linked list matches expected output + # ListNode supports direct equality comparison + assert result == expected From b61ab3dd3bbcf738831a9c26e2c775a4b7c87387 Mon Sep 17 00:00:00 2001 From: Wisaroot Lertthaweedech Date: Thu, 4 Sep 2025 21:18:39 +0700 Subject: [PATCH 3/6] feat: add Coin Change --- .templates/leetcode/json/coin_change.json | 46 ++++++++++ Makefile | 2 +- leetcode/coin_change/README.md | 46 ++++++++++ leetcode/coin_change/__init__.py | 0 leetcode/coin_change/playground.ipynb | 80 +++++++++++++++++ leetcode/coin_change/solution.py | 21 +++++ leetcode/coin_change/tests.py | 31 +++++++ leetcode/reverse_linked_list/playground.ipynb | 88 ++++++++++++++++--- 8 files changed, 299 insertions(+), 15 deletions(-) create mode 100644 .templates/leetcode/json/coin_change.json create mode 100644 leetcode/coin_change/README.md create mode 100644 leetcode/coin_change/__init__.py create mode 100644 leetcode/coin_change/playground.ipynb create mode 100644 leetcode/coin_change/solution.py create mode 100644 leetcode/coin_change/tests.py diff --git a/.templates/leetcode/json/coin_change.json b/.templates/leetcode/json/coin_change.json new file mode 100644 index 0000000..be77578 --- /dev/null +++ b/.templates/leetcode/json/coin_change.json @@ -0,0 +1,46 @@ +{ + "problem_name": "coin_change", + "solution_class_name": "Solution", + "problem_number": "322", + "problem_title": "Coin Change", + "difficulty": "Medium", + "topics": "Array, Dynamic Programming, Breadth-First Search", + "tags": [], + "readme_description": "You are given an integer array `coins` representing coins of different denominations and an integer `amount` representing a total amount of money.\n\nReturn the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return `-1`.\n\nYou may assume that you have an infinite number of each kind of coin.", + "readme_examples": [ + { + "content": "```\nInput: coins = [1,2,5], amount = 11\nOutput: 3\n```\n**Explanation:** 11 = 5 + 5 + 1" + }, + { "content": "```\nInput: coins = [2], amount = 3\nOutput: -1\n```" }, + { "content": "```\nInput: coins = [1], amount = 0\nOutput: 0\n```" } + ], + "readme_constraints": "- `1 <= coins.length <= 12`\n- `1 <= coins[i] <= 2^31 - 1`\n- `0 <= amount <= 10^4`", + "readme_additional": "", + "solution_imports": "", + "solution_methods": [ + { + "name": "coin_change", + "parameters": "coins: list[int], amount: int", + "return_type": "int", + "dummy_return": "-1" + } + ], + "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution", + "test_class_name": "CoinChange", + "test_helper_methods": [ + { "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" } + ], + "test_methods": [ + { + "name": "test_coin_change", + "parametrize": "coins, amount, expected", + "parametrize_typed": "coins: list[int], amount: int, expected: int", + "test_cases": "[([1, 2, 5], 11, 3), ([2], 3, -1), ([1], 0, 0), ([1, 3, 4], 6, 2), ([2, 5, 10, 1], 27, 4), ([5], 3, -1), ([1], 1, 1), ([1, 2], 2, 1), ([186, 419, 83, 408], 6249, 20)]", + "body": "result = self.solution.coin_change(coins, amount)\nassert result == expected" + } + ], + "playground_imports": "from solution import Solution", + "playground_test_case": "# Example test case\ncoins = [1, 2, 5]\namount = 11\nexpected = 3", + "playground_execution": "result = Solution().coin_change(coins, amount)\nresult", + "playground_assertion": "assert result == expected" +} diff --git a/Makefile b/Makefile index a94d3bd..990405c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PYTHON_VERSION = 3.13 -PROBLEM ?= reverse_linked_list +PROBLEM ?= coin_change FORCE ?= 0 COMMA := , diff --git a/leetcode/coin_change/README.md b/leetcode/coin_change/README.md new file mode 100644 index 0000000..5195df6 --- /dev/null +++ b/leetcode/coin_change/README.md @@ -0,0 +1,46 @@ +# Coin Change + +**Difficulty:** Medium +**Topics:** Array, Dynamic Programming, Breadth-First Search +**Tags:** + +**LeetCode:** [Problem 322](https://leetcode.com/problems/coin-change/description/) + +## Problem Description + +You are given an integer array `coins` representing coins of different denominations and an integer `amount` representing a total amount of money. + +Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return `-1`. + +You may assume that you have an infinite number of each kind of coin. + +## Examples + +### Example 1: + +``` +Input: coins = [1,2,5], amount = 11 +Output: 3 +``` + +**Explanation:** 11 = 5 + 5 + 1 + +### Example 2: + +``` +Input: coins = [2], amount = 3 +Output: -1 +``` + +### Example 3: + +``` +Input: coins = [1], amount = 0 +Output: 0 +``` + +## Constraints + +- `1 <= coins.length <= 12` +- `1 <= coins[i] <= 2^31 - 1` +- `0 <= amount <= 10^4` diff --git a/leetcode/coin_change/__init__.py b/leetcode/coin_change/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/leetcode/coin_change/playground.ipynb b/leetcode/coin_change/playground.ipynb new file mode 100644 index 0000000..3c2d8e4 --- /dev/null +++ b/leetcode/coin_change/playground.ipynb @@ -0,0 +1,80 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "imports", + "metadata": {}, + "outputs": [], + "source": [ + "from solution import Solution" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "setup", + "metadata": {}, + "outputs": [], + "source": [ + "# Example test case\n", + "coins = [1, 2, 5]\n", + "amount = 11\n", + "expected = 3" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "execute", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result = Solution().coin_change(coins, amount)\n", + "result" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "test", + "metadata": {}, + "outputs": [], + "source": [ + "assert 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/coin_change/solution.py b/leetcode/coin_change/solution.py new file mode 100644 index 0000000..f808613 --- /dev/null +++ b/leetcode/coin_change/solution.py @@ -0,0 +1,21 @@ +class Solution: + # Time: O(amount * len(coins)) + # Space: O(amount) + def coin_change(self, coins: list[int], amount: int) -> int: + if amount == 0: + return 0 + + # Initialize dp array with amount + 1 (impossible value) + # Since max coins needed is amount (using all 1-cent coins) + # amount + 1 serves as "infinity" to indicate impossible cases + dp = [amount + 1] * (amount + 1) + + dp[0] = 0 + + for i in range(1, amount + 1): + for coin in coins: + if coin <= i: + dp[i] = min(dp[i], dp[i - coin] + 1) + + # Return result: -1 if impossible, otherwise minimum coins needed + return dp[amount] if dp[amount] <= amount else -1 diff --git a/leetcode/coin_change/tests.py b/leetcode/coin_change/tests.py new file mode 100644 index 0000000..50e7c63 --- /dev/null +++ b/leetcode/coin_change/tests.py @@ -0,0 +1,31 @@ +import pytest + +from leetcode_py.test_utils import logged_test + +from .solution import Solution + + +class TestCoinChange: + def setup_method(self): + self.solution = Solution() + + @pytest.mark.parametrize( + "coins, amount, expected", + [ + ([1, 2, 5], 11, 3), + ([2], 3, -1), + ([1], 0, 0), + ([1, 3, 4], 6, 2), + ([2, 5, 10, 1], 27, 4), + ([10, 1, 2, 5], 27, 4), + ([5], 3, -1), + ([5, 2], 3, -1), + ([1], 1, 1), + ([1, 2], 2, 1), + ([186, 419, 83, 408], 6249, 20), + ], + ) + @logged_test + def test_coin_change(self, coins: list[int], amount: int, expected: int): + result = self.solution.coin_change(coins, amount) + assert result == expected diff --git a/leetcode/reverse_linked_list/playground.ipynb b/leetcode/reverse_linked_list/playground.ipynb index 71f31da..140f09b 100644 --- a/leetcode/reverse_linked_list/playground.ipynb +++ b/leetcode/reverse_linked_list/playground.ipynb @@ -22,8 +22,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "Original: [1, 2, 3, 4, 5]\n", - "Expected: [5, 4, 3, 2, 1]\n" + "Original: 1 -> 2 -> 3 -> 4 -> 5\n", + "Expected: 5 -> 4 -> 3 -> 2 -> 1\n" ] } ], @@ -41,7 +41,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "execute", "metadata": {}, "outputs": [ @@ -49,13 +49,81 @@ "name": "stdout", "output_type": "stream", "text": [ - "Result: [5, 4, 3, 2, 1]\n" + "Result: 5 -> 4 -> 3 -> 2 -> 1\n" ] }, { "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "node_0\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "node_1\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "node_0->node_1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "node_2\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "node_1->node_2\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "node_3\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "node_2->node_3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "node_4\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "node_3->node_4\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], "text/plain": [ - "[5, 4, 3, 2, 1]" + "ListNode([5, 4, 3, 2, 1])" ] }, "execution_count": 3, @@ -75,15 +143,7 @@ "execution_count": 4, "id": "test", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "✅ Test passed! Linked list successfully reversed.\n" - ] - } - ], + "outputs": [], "source": [ "# Verify the result matches expected output\n", "assert result == expected" From 9af84ee787049d3a474b849a7e4998143e6f0124 Mon Sep 17 00:00:00 2001 From: Wisaroot Lertthaweedech Date: Thu, 4 Sep 2025 21:37:02 +0700 Subject: [PATCH 4/6] feat: add Largest Rectangle in Histogram --- .templates/leetcode/json/coin_change.json | 2 +- .../json/largest_rectangle_in_histogram.json | 47 +++++++++++ Makefile | 2 +- leetcode/coin_change/README.md | 2 +- .../largest_rectangle_in_histogram/README.md | 38 +++++++++ .../__init__.py | 0 .../playground.ipynb | 79 +++++++++++++++++++ .../solution.py | 29 +++++++ .../largest_rectangle_in_histogram/tests.py | 46 +++++++++++ 9 files changed, 242 insertions(+), 3 deletions(-) create mode 100644 .templates/leetcode/json/largest_rectangle_in_histogram.json create mode 100644 leetcode/largest_rectangle_in_histogram/README.md create mode 100644 leetcode/largest_rectangle_in_histogram/__init__.py create mode 100644 leetcode/largest_rectangle_in_histogram/playground.ipynb create mode 100644 leetcode/largest_rectangle_in_histogram/solution.py create mode 100644 leetcode/largest_rectangle_in_histogram/tests.py diff --git a/.templates/leetcode/json/coin_change.json b/.templates/leetcode/json/coin_change.json index be77578..61ebbc8 100644 --- a/.templates/leetcode/json/coin_change.json +++ b/.templates/leetcode/json/coin_change.json @@ -5,7 +5,7 @@ "problem_title": "Coin Change", "difficulty": "Medium", "topics": "Array, Dynamic Programming, Breadth-First Search", - "tags": [], + "tags": ["grind-75"], "readme_description": "You are given an integer array `coins` representing coins of different denominations and an integer `amount` representing a total amount of money.\n\nReturn the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return `-1`.\n\nYou may assume that you have an infinite number of each kind of coin.", "readme_examples": [ { diff --git a/.templates/leetcode/json/largest_rectangle_in_histogram.json b/.templates/leetcode/json/largest_rectangle_in_histogram.json new file mode 100644 index 0000000..8ebbd85 --- /dev/null +++ b/.templates/leetcode/json/largest_rectangle_in_histogram.json @@ -0,0 +1,47 @@ +{ + "problem_name": "largest_rectangle_in_histogram", + "solution_class_name": "Solution", + "problem_number": "84", + "problem_title": "Largest Rectangle in Histogram", + "difficulty": "Hard", + "topics": "Array, Stack, Monotonic Stack", + "tags": ["grind-75"], + "readme_description": "Given an array of integers `heights` representing the histogram's bar height where the width of each bar is `1`, return the area of the largest rectangle in the histogram.", + "readme_examples": [ + { + "content": "![Example 1](https://assets.leetcode.com/uploads/2021/01/04/histogram.jpg)\n\n```\nInput: heights = [2,1,5,6,2,3]\nOutput: 10\n```\n**Explanation:** The above is a histogram where width of each bar is 1. The largest rectangle is shown in the red area, which has an area = 10 units." + }, + { + "content": "![Example 2](https://assets.leetcode.com/uploads/2021/01/04/histogram-1.jpg)\n\n```\nInput: heights = [2,4]\nOutput: 4\n```" + } + ], + "readme_constraints": "- `1 <= heights.length <= 10^5`\n- `0 <= heights[i] <= 10^4`", + "readme_additional": "", + "solution_imports": "", + "solution_methods": [ + { + "name": "largest_rectangle_area", + "parameters": "heights: list[int]", + "return_type": "int", + "dummy_return": "0" + } + ], + "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution", + "test_class_name": "LargestRectangleInHistogram", + "test_helper_methods": [ + { "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" } + ], + "test_methods": [ + { + "name": "test_largest_rectangle_area", + "parametrize": "heights, expected", + "parametrize_typed": "heights: list[int], expected: int", + "test_cases": "[([2, 1, 5, 6, 2, 3], 10), ([2, 4], 4), ([1], 1), ([0], 0), ([1, 1], 2), ([0, 0, 0], 0), ([1, 2, 3, 4, 5], 9), ([5, 4, 3, 2, 1], 9), ([3, 3, 3, 3], 12), ([2, 1, 2], 3), ([1, 3, 1], 3), ([6, 7, 5, 2, 4, 5, 9, 3], 16), ([4, 2, 0, 3, 2, 5], 6), ([1, 2, 2, 1], 4), ([0, 9], 9), ([9, 0], 9), ([2, 1, 5, 6, 2, 3, 1, 5, 6, 2], 10), ([1, 8, 6, 2, 5, 4, 8, 3, 7], 16)]", + "body": "result = self.solution.largest_rectangle_area(heights)\nassert result == expected" + } + ], + "playground_imports": "from solution import Solution", + "playground_test_case": "# Example test case\nheights = [2, 1, 5, 6, 2, 3]\nexpected = 10", + "playground_execution": "result = Solution().largest_rectangle_area(heights)\nresult", + "playground_assertion": "assert result == expected" +} diff --git a/Makefile b/Makefile index 990405c..7605987 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PYTHON_VERSION = 3.13 -PROBLEM ?= coin_change +PROBLEM ?= largest_rectangle_in_histogram FORCE ?= 0 COMMA := , diff --git a/leetcode/coin_change/README.md b/leetcode/coin_change/README.md index 5195df6..54b005e 100644 --- a/leetcode/coin_change/README.md +++ b/leetcode/coin_change/README.md @@ -2,7 +2,7 @@ **Difficulty:** Medium **Topics:** Array, Dynamic Programming, Breadth-First Search -**Tags:** +**Tags:** grind-75 **LeetCode:** [Problem 322](https://leetcode.com/problems/coin-change/description/) diff --git a/leetcode/largest_rectangle_in_histogram/README.md b/leetcode/largest_rectangle_in_histogram/README.md new file mode 100644 index 0000000..ac89d7c --- /dev/null +++ b/leetcode/largest_rectangle_in_histogram/README.md @@ -0,0 +1,38 @@ +# Largest Rectangle in Histogram + +**Difficulty:** Hard +**Topics:** Array, Stack, Monotonic Stack +**Tags:** grind-75 + +**LeetCode:** [Problem 84](https://leetcode.com/problems/largest-rectangle-in-histogram/description/) + +## Problem Description + +Given an array of integers `heights` representing the histogram's bar height where the width of each bar is `1`, return the area of the largest rectangle in the histogram. + +## Examples + +### Example 1: + +![Example 1](https://assets.leetcode.com/uploads/2021/01/04/histogram.jpg) + +``` +Input: heights = [2,1,5,6,2,3] +Output: 10 +``` + +**Explanation:** The above is a histogram where width of each bar is 1. The largest rectangle is shown in the red area, which has an area = 10 units. + +### Example 2: + +![Example 2](https://assets.leetcode.com/uploads/2021/01/04/histogram-1.jpg) + +``` +Input: heights = [2,4] +Output: 4 +``` + +## Constraints + +- `1 <= heights.length <= 10^5` +- `0 <= heights[i] <= 10^4` diff --git a/leetcode/largest_rectangle_in_histogram/__init__.py b/leetcode/largest_rectangle_in_histogram/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/leetcode/largest_rectangle_in_histogram/playground.ipynb b/leetcode/largest_rectangle_in_histogram/playground.ipynb new file mode 100644 index 0000000..74de151 --- /dev/null +++ b/leetcode/largest_rectangle_in_histogram/playground.ipynb @@ -0,0 +1,79 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "imports", + "metadata": {}, + "outputs": [], + "source": [ + "from solution import Solution" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "setup", + "metadata": {}, + "outputs": [], + "source": [ + "# Example test case\n", + "heights = [2, 1, 5, 6, 2, 3]\n", + "expected = 10" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "execute", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result = Solution().largest_rectangle_area(heights)\n", + "result" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "test", + "metadata": {}, + "outputs": [], + "source": [ + "assert 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/largest_rectangle_in_histogram/solution.py b/leetcode/largest_rectangle_in_histogram/solution.py new file mode 100644 index 0000000..c500980 --- /dev/null +++ b/leetcode/largest_rectangle_in_histogram/solution.py @@ -0,0 +1,29 @@ +class Solution: + # Time: O(n) + # Space: O(n) + def largest_rectangle_area(self, heights: list[int]) -> int: + # Monotonic stack approach + # Stack stores indices of bars in increasing height order + # When we find a shorter bar, we calculate area using previous bars + + stack: list[int] = [] # Stack of indices + max_area = 0 + + for i, height in enumerate(heights): + # While current height is less than stack top height + # Pop from stack and calculate area with popped height as smallest + while stack and heights[stack[-1]] > height: + max_area = max(max_area, self.calculate_area(heights, stack, i)) + + stack.append(i) + + while stack: + max_area = max(max_area, self.calculate_area(heights, stack, len(heights))) + + return max_area + + @staticmethod + def calculate_area(heights: list[int], stack: list[int], right_bound: int) -> int: + h = heights[stack.pop()] + w = right_bound if not stack else right_bound - stack[-1] - 1 + return h * w diff --git a/leetcode/largest_rectangle_in_histogram/tests.py b/leetcode/largest_rectangle_in_histogram/tests.py new file mode 100644 index 0000000..715712f --- /dev/null +++ b/leetcode/largest_rectangle_in_histogram/tests.py @@ -0,0 +1,46 @@ +import pytest + +from leetcode_py.test_utils import logged_test + +from .solution import Solution + + +class TestLargestRectangleInHistogram: + def setup_method(self): + self.solution = Solution() + + @pytest.mark.parametrize( + "heights, expected", + [ + # Basic examples + ([2, 1, 5, 6, 2, 3], 10), + ([2, 4], 4), + # Edge cases + ([1], 1), + ([0], 0), + ([1, 1], 2), + ([0, 0, 0], 0), + # Patterns + ([1, 2, 3, 4, 5], 9), + ([5, 4, 3, 2, 1], 9), + ([3, 3, 3, 3], 12), + ([2, 1, 2], 3), + ([1, 3, 1], 3), + # Complex cases + ([6, 7, 5, 2, 4, 5, 9, 3], 16), + ([4, 2, 0, 3, 2, 5], 6), + ([1, 2, 2, 1], 4), + ([0, 9], 9), + ([9, 0], 9), + # Large rectangles + ([2, 1, 5, 6, 2, 3, 1, 5, 6, 2], 10), + ([1, 8, 6, 2, 5, 4, 8, 3, 7], 16), + ([50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 50), + ([1, 1, 1, 1, 1, 50, 1, 1, 1, 1, 1], 50), + ([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 50], 50), + ], + ) + @logged_test + def test_largest_rectangle_area(self, heights: list[int], expected: int): + result = self.solution.largest_rectangle_area(heights) + assert result == expected From 4ba5bdd41875e448216502d175c69affaf0d7a7b Mon Sep 17 00:00:00 2001 From: Wisaroot Lertthaweedech Date: Thu, 4 Sep 2025 21:53:22 +0700 Subject: [PATCH 5/6] feat: add Trapping Rain Water --- .../leetcode/json/trapping_rain_water.json | 40 ++++++++++ Makefile | 2 +- leetcode/trapping_rain_water/README.md | 37 +++++++++ leetcode/trapping_rain_water/__init__.py | 0 leetcode/trapping_rain_water/playground.ipynb | 79 +++++++++++++++++++ leetcode/trapping_rain_water/solution.py | 52 ++++++++++++ leetcode/trapping_rain_water/tests.py | 42 ++++++++++ 7 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 .templates/leetcode/json/trapping_rain_water.json create mode 100644 leetcode/trapping_rain_water/README.md create mode 100644 leetcode/trapping_rain_water/__init__.py create mode 100644 leetcode/trapping_rain_water/playground.ipynb create mode 100644 leetcode/trapping_rain_water/solution.py create mode 100644 leetcode/trapping_rain_water/tests.py diff --git a/.templates/leetcode/json/trapping_rain_water.json b/.templates/leetcode/json/trapping_rain_water.json new file mode 100644 index 0000000..dfdb0bb --- /dev/null +++ b/.templates/leetcode/json/trapping_rain_water.json @@ -0,0 +1,40 @@ +{ + "problem_name": "trapping_rain_water", + "solution_class_name": "Solution", + "problem_number": "42", + "problem_title": "Trapping Rain Water", + "difficulty": "Hard", + "topics": "Array, Two Pointers, Dynamic Programming, Stack, Monotonic Stack", + "tags": ["grind-75"], + "readme_description": "Given `n` non-negative integers representing an elevation map where the width of each bar is `1`, compute how much water it can trap after raining.", + "readme_examples": [ + { + "content": "![Example 1](https://assets.leetcode.com/uploads/2018/10/22/rainwatertrap.png)\n\n```\nInput: height = [0,1,0,2,1,0,1,3,2,1,2,1]\nOutput: 6\n```\n**Explanation:** The above elevation map (black section) is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped." + }, + { "content": "```\nInput: height = [4,2,0,3,2,5]\nOutput: 9\n```" } + ], + "readme_constraints": "- `n == height.length`\n- `1 <= n <= 2 * 10^4`\n- `0 <= height[i] <= 10^5`", + "readme_additional": "", + "solution_imports": "", + "solution_methods": [ + { "name": "trap", "parameters": "height: list[int]", "return_type": "int", "dummy_return": "0" } + ], + "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution", + "test_class_name": "TrappingRainWater", + "test_helper_methods": [ + { "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" } + ], + "test_methods": [ + { + "name": "test_trap", + "parametrize": "height, expected", + "parametrize_typed": "height: list[int], expected: int", + "test_cases": "[([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1], 6), ([4, 2, 0, 3, 2, 5], 9), ([3, 0, 2, 0, 4], 7), ([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1], 6)]", + "body": "result = self.solution.trap(height)\nassert result == expected" + } + ], + "playground_imports": "from solution import Solution", + "playground_test_case": "# Example test case\nheight = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]\nexpected = 6", + "playground_execution": "result = Solution().trap(height)\nresult", + "playground_assertion": "assert result == expected" +} diff --git a/Makefile b/Makefile index 7605987..a3d209d 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PYTHON_VERSION = 3.13 -PROBLEM ?= largest_rectangle_in_histogram +PROBLEM ?= trapping_rain_water FORCE ?= 0 COMMA := , diff --git a/leetcode/trapping_rain_water/README.md b/leetcode/trapping_rain_water/README.md new file mode 100644 index 0000000..fc3a317 --- /dev/null +++ b/leetcode/trapping_rain_water/README.md @@ -0,0 +1,37 @@ +# Trapping Rain Water + +**Difficulty:** Hard +**Topics:** Array, Two Pointers, Dynamic Programming, Stack, Monotonic Stack +**Tags:** grind-75 + +**LeetCode:** [Problem 42](https://leetcode.com/problems/trapping-rain-water/description/) + +## Problem Description + +Given `n` non-negative integers representing an elevation map where the width of each bar is `1`, compute how much water it can trap after raining. + +## Examples + +### Example 1: + +![Example 1](https://assets.leetcode.com/uploads/2018/10/22/rainwatertrap.png) + +``` +Input: height = [0,1,0,2,1,0,1,3,2,1,2,1] +Output: 6 +``` + +**Explanation:** The above elevation map (black section) is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. + +### Example 2: + +``` +Input: height = [4,2,0,3,2,5] +Output: 9 +``` + +## Constraints + +- `n == height.length` +- `1 <= n <= 2 * 10^4` +- `0 <= height[i] <= 10^5` diff --git a/leetcode/trapping_rain_water/__init__.py b/leetcode/trapping_rain_water/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/leetcode/trapping_rain_water/playground.ipynb b/leetcode/trapping_rain_water/playground.ipynb new file mode 100644 index 0000000..be2bf9a --- /dev/null +++ b/leetcode/trapping_rain_water/playground.ipynb @@ -0,0 +1,79 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "imports", + "metadata": {}, + "outputs": [], + "source": [ + "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": "execute", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result = Solution().trap(height)\n", + "result" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "test", + "metadata": {}, + "outputs": [], + "source": [ + "assert 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/solution.py b/leetcode/trapping_rain_water/solution.py new file mode 100644 index 0000000..dd84800 --- /dev/null +++ b/leetcode/trapping_rain_water/solution.py @@ -0,0 +1,52 @@ +# class Solution: +# # Time: O(n) +# # Space: O(1) +# def trap(self, height: list[int]) -> int: +# if not height: +# return 0 + +# left, right = 0, len(height) - 1 +# left_max = right_max = water = 0 + +# while left < right: +# if height[left] < height[right]: +# if height[left] >= left_max: +# left_max = height[left] +# else: +# water += left_max - height[left] +# left += 1 +# else: +# if height[right] >= right_max: +# right_max = height[right] +# else: +# water += right_max - height[right] +# right -= 1 + +# return water + + +class Solution: + # Time: O(n) + # Space: O(1) + def trap(self, height: list[int]) -> int: + if not height: + return 0 + + left, right = 0, len(height) - 1 + left_max = right_max = water = 0 + + while left < right: + if height[left] < height[right]: + if height[left] >= left_max: + left_max = height[left] + else: + water += left_max - height[left] + left += 1 + else: + if height[right] >= right_max: + right_max = height[right] + else: + water += right_max - height[right] + right -= 1 + + return water diff --git a/leetcode/trapping_rain_water/tests.py b/leetcode/trapping_rain_water/tests.py new file mode 100644 index 0000000..2a3b912 --- /dev/null +++ b/leetcode/trapping_rain_water/tests.py @@ -0,0 +1,42 @@ +import pytest + +from leetcode_py.test_utils import logged_test + +from .solution import Solution + + +class TestTrappingRainWater: + def setup_method(self): + self.solution = Solution() + + @pytest.mark.parametrize( + "height, expected", + [ + # Original examples + ([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1], 6), + ([4, 2, 0, 3, 2, 5], 9), + ([3, 0, 2, 0, 4], 7), + # Edge cases + ([], 0), + ([1], 0), + ([1, 2], 0), + ([2, 1], 0), + # No water trapped + ([1, 2, 3, 4, 5], 0), + ([5, 4, 3, 2, 1], 0), + # Simple cases + ([3, 0, 3], 3), + ([2, 1, 2], 1), + ([5, 2, 7, 2, 6, 1, 5, 3, 2, 4], 14), + # All same height + ([3, 3, 3, 3], 0), + # Valley pattern + ([3, 2, 1, 2, 3], 4), + # Multiple peaks + ([0, 2, 0, 4, 0, 3, 0, 4, 0, 2, 0], 13), + ], + ) + @logged_test + def test_trap(self, height: list[int], expected: int): + result = self.solution.trap(height) + assert result == expected From fd4741d4ef7065b48e86512b96ee8fc692d26b36 Mon Sep 17 00:00:00 2001 From: Wisaroot Lertthaweedech Date: Thu, 4 Sep 2025 22:15:02 +0700 Subject: [PATCH 6/6] feat: add maximum_profit_in_job_scheduling --- .../maximum_profit_in_job_scheduling.json | 50 +++++ Makefile | 2 +- .../playground.ipynb | 20 +- leetcode/longest_palindrome/playground.ipynb | 3 +- .../README.md | 54 ++++++ .../__init__.py | 0 .../playground.ipynb | 174 ++++++++++++++++++ .../solution.py | 51 +++++ .../maximum_profit_in_job_scheduling/tests.py | 34 ++++ leetcode/maximum_subarray/playground.ipynb | 19 +- leetcode/merge_intervals/playground.ipynb | 3 +- leetcode/valid_palindrome/playground.ipynb | 19 +- 12 files changed, 414 insertions(+), 15 deletions(-) create mode 100644 .templates/leetcode/json/maximum_profit_in_job_scheduling.json create mode 100644 leetcode/maximum_profit_in_job_scheduling/README.md create mode 100644 leetcode/maximum_profit_in_job_scheduling/__init__.py create mode 100644 leetcode/maximum_profit_in_job_scheduling/playground.ipynb create mode 100644 leetcode/maximum_profit_in_job_scheduling/solution.py create mode 100644 leetcode/maximum_profit_in_job_scheduling/tests.py diff --git a/.templates/leetcode/json/maximum_profit_in_job_scheduling.json b/.templates/leetcode/json/maximum_profit_in_job_scheduling.json new file mode 100644 index 0000000..53b2f7c --- /dev/null +++ b/.templates/leetcode/json/maximum_profit_in_job_scheduling.json @@ -0,0 +1,50 @@ +{ + "problem_name": "maximum_profit_in_job_scheduling", + "solution_class_name": "Solution", + "problem_number": "1235", + "problem_title": "Maximum Profit in Job Scheduling", + "difficulty": "Hard", + "topics": "Array, Binary Search, Dynamic Programming, Sorting", + "tags": ["grind-75"], + "readme_description": "We have `n` jobs, where every job is scheduled to be done from `startTime[i]` to `endTime[i]`, obtaining a profit of `profit[i]`.\n\nYou're given the `startTime`, `endTime` and `profit` arrays, return the maximum profit you can take such that there are no two jobs in the subset with overlapping time range.\n\nIf you choose a job that ends at time `X` you will be able to start another job that starts at time `X`.", + "readme_examples": [ + { + "content": "![Example 1](https://assets.leetcode.com/uploads/2019/10/10/sample1_1584.png)\n\n```\nInput: startTime = [1,2,3,3], endTime = [3,4,5,6], profit = [50,10,40,70]\nOutput: 120\n```\n**Explanation:** The subset chosen is the first and fourth job. Time range [1-3]+[3-6] , we get profit of 120 = 50 + 70." + }, + { + "content": "![Example 2](https://assets.leetcode.com/uploads/2019/10/10/sample22_1584.png)\n\n```\nInput: startTime = [1,2,3,4,6], endTime = [3,5,10,6,9], profit = [20,20,100,70,60]\nOutput: 150\n```\n**Explanation:** The subset chosen is the first, fourth and fifth job. Profit obtained 150 = 20 + 70 + 60." + }, + { + "content": "![Example 3](https://assets.leetcode.com/uploads/2019/10/10/sample3_1584.png)\n\n```\nInput: startTime = [1,1,1], endTime = [2,3,4], profit = [5,6,4]\nOutput: 6\n```" + } + ], + "readme_constraints": "- `1 <= startTime.length == endTime.length == profit.length <= 5 * 10^4`\n- `1 <= startTime[i] < endTime[i] <= 10^9`\n- `1 <= profit[i] <= 10^4`", + "readme_additional": "", + "solution_imports": "", + "solution_methods": [ + { + "name": "job_scheduling", + "parameters": "start_time: list[int], end_time: list[int], profit: list[int]", + "return_type": "int", + "dummy_return": "0" + } + ], + "test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution", + "test_class_name": "MaximumProfitInJobScheduling", + "test_helper_methods": [ + { "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" } + ], + "test_methods": [ + { + "name": "test_job_scheduling", + "parametrize": "start_time, end_time, profit, expected", + "parametrize_typed": "start_time: list[int], end_time: list[int], profit: list[int], expected: int", + "test_cases": "[([1, 2, 3, 3], [3, 4, 5, 6], [50, 10, 40, 70], 120), ([1, 2, 3, 4, 6], [3, 5, 10, 6, 9], [20, 20, 100, 70, 60], 150), ([1, 1, 1], [2, 3, 4], [5, 6, 4], 6)]", + "body": "result = self.solution.job_scheduling(start_time, end_time, profit)\nassert result == expected" + } + ], + "playground_imports": "from solution import Solution", + "playground_test_case": "# Example test case\nstart_time = [1, 2, 3, 3]\nend_time = [3, 4, 5, 6]\nprofit = [50, 10, 40, 70]\nexpected = 120", + "playground_execution": "result = Solution().job_scheduling(start_time, end_time, profit)\nresult", + "playground_assertion": "assert result == expected" +} diff --git a/Makefile b/Makefile index a3d209d..a433355 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PYTHON_VERSION = 3.13 -PROBLEM ?= trapping_rain_water +PROBLEM ?= maximum_profit_in_job_scheduling FORCE ?= 0 COMMA := , diff --git a/leetcode/k_closest_points_to_origin/playground.ipynb b/leetcode/k_closest_points_to_origin/playground.ipynb index 49a9238..90da6e9 100644 --- a/leetcode/k_closest_points_to_origin/playground.ipynb +++ b/leetcode/k_closest_points_to_origin/playground.ipynb @@ -6,7 +6,9 @@ "id": "imports", "metadata": {}, "outputs": [], - "source": ["from solution import Solution"] + "source": [ + "from solution import Solution" + ] }, { "cell_type": "code", @@ -14,7 +16,12 @@ "id": "setup", "metadata": {}, "outputs": [], - "source": ["# Example test case\npoints = [[1, 3], [-2, 2]]\nk = 1\nexpected = [[-2, 2]]"] + "source": [ + "# Example test case\n", + "points = [[1, 3], [-2, 2]]\n", + "k = 1\n", + "expected = [[-2, 2]]" + ] }, { "cell_type": "code", @@ -22,7 +29,10 @@ "id": "execute", "metadata": {}, "outputs": [], - "source": ["result = Solution().k_closest(points, k)\nresult"] + "source": [ + "result = Solution().k_closest(points, k)\n", + "result" + ] }, { "cell_type": "code", @@ -30,7 +40,9 @@ "id": "test", "metadata": {}, "outputs": [], - "source": ["assert sorted(result) == sorted(expected)"] + "source": [ + "assert sorted(result) == sorted(expected)" + ] } ], "metadata": { diff --git a/leetcode/longest_palindrome/playground.ipynb b/leetcode/longest_palindrome/playground.ipynb index 8c61bb7..5f53c98 100644 --- a/leetcode/longest_palindrome/playground.ipynb +++ b/leetcode/longest_palindrome/playground.ipynb @@ -29,7 +29,8 @@ "metadata": {}, "outputs": [], "source": [ - "result = Solution().longest_palindrome(s)\nresult" + "result = Solution().longest_palindrome(s)\n", + "result" ] }, { diff --git a/leetcode/maximum_profit_in_job_scheduling/README.md b/leetcode/maximum_profit_in_job_scheduling/README.md new file mode 100644 index 0000000..100bfa7 --- /dev/null +++ b/leetcode/maximum_profit_in_job_scheduling/README.md @@ -0,0 +1,54 @@ +# Maximum Profit in Job Scheduling + +**Difficulty:** Hard +**Topics:** Array, Binary Search, Dynamic Programming, Sorting +**Tags:** grind-75 + +**LeetCode:** [Problem 1235](https://leetcode.com/problems/maximum-profit-in-job-scheduling/description/) + +## Problem Description + +We have `n` jobs, where every job is scheduled to be done from `startTime[i]` to `endTime[i]`, obtaining a profit of `profit[i]`. + +You're given the `startTime`, `endTime` and `profit` arrays, return the maximum profit you can take such that there are no two jobs in the subset with overlapping time range. + +If you choose a job that ends at time `X` you will be able to start another job that starts at time `X`. + +## Examples + +### Example 1: + +![Example 1](https://assets.leetcode.com/uploads/2019/10/10/sample1_1584.png) + +``` +Input: startTime = [1,2,3,3], endTime = [3,4,5,6], profit = [50,10,40,70] +Output: 120 +``` + +**Explanation:** The subset chosen is the first and fourth job. Time range [1-3]+[3-6] , we get profit of 120 = 50 + 70. + +### Example 2: + +![Example 2](https://assets.leetcode.com/uploads/2019/10/10/sample22_1584.png) + +``` +Input: startTime = [1,2,3,4,6], endTime = [3,5,10,6,9], profit = [20,20,100,70,60] +Output: 150 +``` + +**Explanation:** The subset chosen is the first, fourth and fifth job. Profit obtained 150 = 20 + 70 + 60. + +### Example 3: + +![Example 3](https://assets.leetcode.com/uploads/2019/10/10/sample3_1584.png) + +``` +Input: startTime = [1,1,1], endTime = [2,3,4], profit = [5,6,4] +Output: 6 +``` + +## Constraints + +- `1 <= startTime.length == endTime.length == profit.length <= 5 * 10^4` +- `1 <= startTime[i] < endTime[i] <= 10^9` +- `1 <= profit[i] <= 10^4` diff --git a/leetcode/maximum_profit_in_job_scheduling/__init__.py b/leetcode/maximum_profit_in_job_scheduling/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/leetcode/maximum_profit_in_job_scheduling/playground.ipynb b/leetcode/maximum_profit_in_job_scheduling/playground.ipynb new file mode 100644 index 0000000..9499ca9 --- /dev/null +++ b/leetcode/maximum_profit_in_job_scheduling/playground.ipynb @@ -0,0 +1,174 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "imports", + "metadata": {}, + "outputs": [], + "source": [ + "import bisect\n", + "\n", + "from solution import Solution" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "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": 3, + "id": "execute", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "120" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result = Solution().job_scheduling(start_time, end_time, profit)\n", + "result" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "test", + "metadata": {}, + "outputs": [], + "source": [ + "assert result == expected" + ] + }, + { + "cell_type": "markdown", + "id": "dd4ebe51", + "metadata": {}, + "source": [ + "# Bisect" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "3596ff83", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original array: [1, 3, 3, 5, 7]\n", + "Indices: [0, 1, 2, 3, 4]\n", + "\n" + ] + } + ], + "source": [ + "# Demo array\n", + "arr = [1, 3, 3, 5, 7]\n", + "print(f\"Original array: {arr}\")\n", + "print(f\"Indices: {list(range(len(arr)))}\")\n", + "print()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "508a52e9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== bisect_left vs bisect_right ===\n", + "x=0: left=0, right=0\n", + "x=3: left=1, right=3\n", + "x=4: left=3, right=3\n", + "x=8: left=5, right=5\n", + "\n" + ] + } + ], + "source": [ + "# bisect_left vs bisect_right\n", + "print(\"=== bisect_left vs bisect_right ===\")\n", + "for x in [0, 3, 4, 8]:\n", + " left = bisect.bisect_left(arr, x)\n", + " right = bisect.bisect_right(arr, x)\n", + " print(f\"x={x}: left={left}, right={right}\")\n", + "print()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b5dfa28a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== insort demonstration ===\n", + "Before: [1, 3, 5]\n", + "After insort(4): [1, 3, 4, 5]\n", + "After insort(3): [1, 3, 3, 4, 5]\n", + "\n" + ] + } + ], + "source": [ + "# insort demonstration\n", + "print(\"=== insort demonstration ===\")\n", + "test_arr = [1, 3, 5]\n", + "print(f\"Before: {test_arr}\")\n", + "bisect.insort(test_arr, 4)\n", + "print(f\"After insort(4): {test_arr}\")\n", + "bisect.insort(test_arr, 3)\n", + "print(f\"After insort(3): {test_arr}\")\n", + "print()" + ] + } + ], + "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/maximum_profit_in_job_scheduling/solution.py b/leetcode/maximum_profit_in_job_scheduling/solution.py new file mode 100644 index 0000000..6fc5cfc --- /dev/null +++ b/leetcode/maximum_profit_in_job_scheduling/solution.py @@ -0,0 +1,51 @@ +import bisect + + +class Solution: + # Time: O(n log n) + # Space: O(n) + def job_scheduling(self, start_time: list[int], end_time: list[int], profit: list[int]) -> int: + + jobs = sorted(zip(end_time, start_time, profit)) + dp = [0] * len(jobs) + + for i, (end, start, p) in enumerate(jobs): + # Binary search for latest non-overlapping job + j = bisect.bisect_right([job[0] for job in jobs[:i]], start) - 1 + + # Take current job + best profit from non-overlapping jobs + take = p + (dp[j] if j >= 0 else 0) + # Skip current job + skip = dp[i - 1] if i > 0 else 0 + + dp[i] = max(take, skip) + + return dp[-1] if jobs else 0 + + +# bisect and insort Explanation: +# +# Etymology: "bisect" = bi (two) + sect (cut) = cut into two parts +# Bisection method = binary search algorithm that repeatedly cuts search space in half +# +# bisect module provides binary search for SORTED lists (O(log n)): +# - bisect_left(arr, x): leftmost insertion position +# - bisect_right(arr, x): rightmost insertion position (default) +# - bisect(arr, x): alias for bisect_right +# +# insort module maintains sorted order while inserting: +# - insort_left(arr, x): insert at leftmost position +# - insort_right(arr, x): insert at rightmost position (default) +# - insort(arr, x): alias for insort_right +# +# Examples: +# arr = [1, 3, 3, 5] +# bisect_left(arr, 3) → 1 (before existing 3s) +# bisect_right(arr, 3) → 3 (after existing 3s) +# bisect_right(arr, 4) → 3 (between 3 and 5) +# +# insort(arr, 4) → arr becomes [1, 3, 3, 4, 5] +# +# In our solution: +# bisect_right([2,4,6], 5) = 2 (insertion position) +# j = 2 - 1 = 1 (index of latest job ending ≤ start_time) diff --git a/leetcode/maximum_profit_in_job_scheduling/tests.py b/leetcode/maximum_profit_in_job_scheduling/tests.py new file mode 100644 index 0000000..21da4c0 --- /dev/null +++ b/leetcode/maximum_profit_in_job_scheduling/tests.py @@ -0,0 +1,34 @@ +import pytest + +from leetcode_py.test_utils import logged_test + +from .solution import Solution + + +class TestMaximumProfitInJobScheduling: + def setup_method(self): + self.solution = Solution() + + @pytest.mark.parametrize( + "start_time, end_time, profit, expected", + [ + # Original examples + ([1, 2, 3, 3], [3, 4, 5, 6], [50, 10, 40, 70], 120), + ([1, 2, 3, 4, 6], [3, 5, 10, 6, 9], [20, 20, 100, 70, 60], 150), + ([1, 1, 1], [2, 3, 4], [5, 6, 4], 6), + # Edge cases + ([1], [2], [100], 100), # Single job + ([1, 2], [2, 3], [50, 100], 150), # Adjacent jobs + ([1, 3], [2, 4], [50, 100], 150), # Overlapping jobs - can take both + ([1, 3, 5], [2, 4, 6], [10, 20, 30], 60), # No overlaps - take all + ([1, 1, 1], [2, 2, 2], [10, 20, 30], 30), # Same time slots - take best + ([1, 2, 3, 4], [2, 3, 4, 5], [1, 1, 1, 1], 4), # All same profit - take all + ([1, 5, 10], [3, 7, 12], [100, 1, 1], 102), # High profit + non-overlapping + ], + ) + @logged_test + def test_job_scheduling( + self, start_time: list[int], end_time: list[int], profit: list[int], expected: int + ): + result = self.solution.job_scheduling(start_time, end_time, profit) + assert result == expected diff --git a/leetcode/maximum_subarray/playground.ipynb b/leetcode/maximum_subarray/playground.ipynb index 2cbaa51..a34773e 100644 --- a/leetcode/maximum_subarray/playground.ipynb +++ b/leetcode/maximum_subarray/playground.ipynb @@ -6,7 +6,9 @@ "id": "imports", "metadata": {}, "outputs": [], - "source": ["from solution import Solution"] + "source": [ + "from solution import Solution" + ] }, { "cell_type": "code", @@ -14,7 +16,11 @@ "id": "setup", "metadata": {}, "outputs": [], - "source": ["# Example test case\nnums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]\nexpected = 6"] + "source": [ + "# Example test case\n", + "nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]\n", + "expected = 6" + ] }, { "cell_type": "code", @@ -22,7 +28,10 @@ "id": "execute", "metadata": {}, "outputs": [], - "source": ["result = Solution().max_sub_array(nums)\nresult"] + "source": [ + "result = Solution().max_sub_array(nums)\n", + "result" + ] }, { "cell_type": "code", @@ -30,7 +39,9 @@ "id": "test", "metadata": {}, "outputs": [], - "source": ["assert result == expected"] + "source": [ + "assert result == expected" + ] } ], "metadata": { diff --git a/leetcode/merge_intervals/playground.ipynb b/leetcode/merge_intervals/playground.ipynb index ae09a51..99158ad 100644 --- a/leetcode/merge_intervals/playground.ipynb +++ b/leetcode/merge_intervals/playground.ipynb @@ -29,7 +29,8 @@ "metadata": {}, "outputs": [], "source": [ - "result = Solution().merge(intervals)\nresult" + "result = Solution().merge(intervals)\n", + "result" ] }, { diff --git a/leetcode/valid_palindrome/playground.ipynb b/leetcode/valid_palindrome/playground.ipynb index 981f5a4..86aeb30 100644 --- a/leetcode/valid_palindrome/playground.ipynb +++ b/leetcode/valid_palindrome/playground.ipynb @@ -6,7 +6,9 @@ "id": "imports", "metadata": {}, "outputs": [], - "source": ["from solution import Solution"] + "source": [ + "from solution import Solution" + ] }, { "cell_type": "code", @@ -14,7 +16,11 @@ "id": "setup", "metadata": {}, "outputs": [], - "source": ["# Example test case\ns = \"A man, a plan, a canal: Panama\"\nexpected = True"] + "source": [ + "# Example test case\n", + "s = \"A man, a plan, a canal: Panama\"\n", + "expected = True" + ] }, { "cell_type": "code", @@ -22,7 +28,10 @@ "id": "execute", "metadata": {}, "outputs": [], - "source": ["result = Solution().is_palindrome(s)\nresult"] + "source": [ + "result = Solution().is_palindrome(s)\n", + "result" + ] }, { "cell_type": "code", @@ -30,7 +39,9 @@ "id": "test", "metadata": {}, "outputs": [], - "source": ["assert result == expected"] + "source": [ + "assert result == expected" + ] } ], "metadata": {