Skip to content

Commit b61ab3d

Browse files
committed
feat: add Coin Change
1 parent 350404c commit b61ab3d

File tree

8 files changed

+299
-15
lines changed

8 files changed

+299
-15
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"problem_name": "coin_change",
3+
"solution_class_name": "Solution",
4+
"problem_number": "322",
5+
"problem_title": "Coin Change",
6+
"difficulty": "Medium",
7+
"topics": "Array, Dynamic Programming, Breadth-First Search",
8+
"tags": [],
9+
"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.",
10+
"readme_examples": [
11+
{
12+
"content": "```\nInput: coins = [1,2,5], amount = 11\nOutput: 3\n```\n**Explanation:** 11 = 5 + 5 + 1"
13+
},
14+
{ "content": "```\nInput: coins = [2], amount = 3\nOutput: -1\n```" },
15+
{ "content": "```\nInput: coins = [1], amount = 0\nOutput: 0\n```" }
16+
],
17+
"readme_constraints": "- `1 <= coins.length <= 12`\n- `1 <= coins[i] <= 2^31 - 1`\n- `0 <= amount <= 10^4`",
18+
"readme_additional": "",
19+
"solution_imports": "",
20+
"solution_methods": [
21+
{
22+
"name": "coin_change",
23+
"parameters": "coins: list[int], amount: int",
24+
"return_type": "int",
25+
"dummy_return": "-1"
26+
}
27+
],
28+
"test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution",
29+
"test_class_name": "CoinChange",
30+
"test_helper_methods": [
31+
{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }
32+
],
33+
"test_methods": [
34+
{
35+
"name": "test_coin_change",
36+
"parametrize": "coins, amount, expected",
37+
"parametrize_typed": "coins: list[int], amount: int, expected: int",
38+
"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)]",
39+
"body": "result = self.solution.coin_change(coins, amount)\nassert result == expected"
40+
}
41+
],
42+
"playground_imports": "from solution import Solution",
43+
"playground_test_case": "# Example test case\ncoins = [1, 2, 5]\namount = 11\nexpected = 3",
44+
"playground_execution": "result = Solution().coin_change(coins, amount)\nresult",
45+
"playground_assertion": "assert result == expected"
46+
}

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PYTHON_VERSION = 3.13
2-
PROBLEM ?= reverse_linked_list
2+
PROBLEM ?= coin_change
33
FORCE ?= 0
44
COMMA := ,
55

leetcode/coin_change/README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Coin Change
2+
3+
**Difficulty:** Medium
4+
**Topics:** Array, Dynamic Programming, Breadth-First Search
5+
**Tags:**
6+
7+
**LeetCode:** [Problem 322](https://leetcode.com/problems/coin-change/description/)
8+
9+
## Problem Description
10+
11+
You are given an integer array `coins` representing coins of different denominations and an integer `amount` representing a total amount of money.
12+
13+
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`.
14+
15+
You may assume that you have an infinite number of each kind of coin.
16+
17+
## Examples
18+
19+
### Example 1:
20+
21+
```
22+
Input: coins = [1,2,5], amount = 11
23+
Output: 3
24+
```
25+
26+
**Explanation:** 11 = 5 + 5 + 1
27+
28+
### Example 2:
29+
30+
```
31+
Input: coins = [2], amount = 3
32+
Output: -1
33+
```
34+
35+
### Example 3:
36+
37+
```
38+
Input: coins = [1], amount = 0
39+
Output: 0
40+
```
41+
42+
## Constraints
43+
44+
- `1 <= coins.length <= 12`
45+
- `1 <= coins[i] <= 2^31 - 1`
46+
- `0 <= amount <= 10^4`

leetcode/coin_change/__init__.py

Whitespace-only changes.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 1,
6+
"id": "imports",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"from solution import Solution"
11+
]
12+
},
13+
{
14+
"cell_type": "code",
15+
"execution_count": 2,
16+
"id": "setup",
17+
"metadata": {},
18+
"outputs": [],
19+
"source": [
20+
"# Example test case\n",
21+
"coins = [1, 2, 5]\n",
22+
"amount = 11\n",
23+
"expected = 3"
24+
]
25+
},
26+
{
27+
"cell_type": "code",
28+
"execution_count": 3,
29+
"id": "execute",
30+
"metadata": {},
31+
"outputs": [
32+
{
33+
"data": {
34+
"text/plain": [
35+
"3"
36+
]
37+
},
38+
"execution_count": 3,
39+
"metadata": {},
40+
"output_type": "execute_result"
41+
}
42+
],
43+
"source": [
44+
"result = Solution().coin_change(coins, amount)\n",
45+
"result"
46+
]
47+
},
48+
{
49+
"cell_type": "code",
50+
"execution_count": 4,
51+
"id": "test",
52+
"metadata": {},
53+
"outputs": [],
54+
"source": [
55+
"assert result == expected"
56+
]
57+
}
58+
],
59+
"metadata": {
60+
"kernelspec": {
61+
"display_name": "leetcode-py-py3.13",
62+
"language": "python",
63+
"name": "python3"
64+
},
65+
"language_info": {
66+
"codemirror_mode": {
67+
"name": "ipython",
68+
"version": 3
69+
},
70+
"file_extension": ".py",
71+
"mimetype": "text/x-python",
72+
"name": "python",
73+
"nbconvert_exporter": "python",
74+
"pygments_lexer": "ipython3",
75+
"version": "3.13.7"
76+
}
77+
},
78+
"nbformat": 4,
79+
"nbformat_minor": 5
80+
}

leetcode/coin_change/solution.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
class Solution:
2+
# Time: O(amount * len(coins))
3+
# Space: O(amount)
4+
def coin_change(self, coins: list[int], amount: int) -> int:
5+
if amount == 0:
6+
return 0
7+
8+
# Initialize dp array with amount + 1 (impossible value)
9+
# Since max coins needed is amount (using all 1-cent coins)
10+
# amount + 1 serves as "infinity" to indicate impossible cases
11+
dp = [amount + 1] * (amount + 1)
12+
13+
dp[0] = 0
14+
15+
for i in range(1, amount + 1):
16+
for coin in coins:
17+
if coin <= i:
18+
dp[i] = min(dp[i], dp[i - coin] + 1)
19+
20+
# Return result: -1 if impossible, otherwise minimum coins needed
21+
return dp[amount] if dp[amount] <= amount else -1

leetcode/coin_change/tests.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import pytest
2+
3+
from leetcode_py.test_utils import logged_test
4+
5+
from .solution import Solution
6+
7+
8+
class TestCoinChange:
9+
def setup_method(self):
10+
self.solution = Solution()
11+
12+
@pytest.mark.parametrize(
13+
"coins, amount, expected",
14+
[
15+
([1, 2, 5], 11, 3),
16+
([2], 3, -1),
17+
([1], 0, 0),
18+
([1, 3, 4], 6, 2),
19+
([2, 5, 10, 1], 27, 4),
20+
([10, 1, 2, 5], 27, 4),
21+
([5], 3, -1),
22+
([5, 2], 3, -1),
23+
([1], 1, 1),
24+
([1, 2], 2, 1),
25+
([186, 419, 83, 408], 6249, 20),
26+
],
27+
)
28+
@logged_test
29+
def test_coin_change(self, coins: list[int], amount: int, expected: int):
30+
result = self.solution.coin_change(coins, amount)
31+
assert result == expected

leetcode/reverse_linked_list/playground.ipynb

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
"name": "stdout",
2323
"output_type": "stream",
2424
"text": [
25-
"Original: [1, 2, 3, 4, 5]\n",
26-
"Expected: [5, 4, 3, 2, 1]\n"
25+
"Original: 1 -> 2 -> 3 -> 4 -> 5\n",
26+
"Expected: 5 -> 4 -> 3 -> 2 -> 1\n"
2727
]
2828
}
2929
],
@@ -41,21 +41,89 @@
4141
},
4242
{
4343
"cell_type": "code",
44-
"execution_count": null,
44+
"execution_count": 3,
4545
"id": "execute",
4646
"metadata": {},
4747
"outputs": [
4848
{
4949
"name": "stdout",
5050
"output_type": "stream",
5151
"text": [
52-
"Result: [5, 4, 3, 2, 1]\n"
52+
"Result: 5 -> 4 -> 3 -> 2 -> 1\n"
5353
]
5454
},
5555
{
5656
"data": {
57+
"text/html": [
58+
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
59+
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
60+
" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
61+
"<!-- Generated by graphviz version 13.1.2 (20250808.2320)\n",
62+
" -->\n",
63+
"<!-- Pages: 1 -->\n",
64+
"<svg width=\"422pt\" height=\"44pt\"\n",
65+
" viewBox=\"0.00 0.00 422.00 44.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
66+
"<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 40)\">\n",
67+
"<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-40 418,-40 418,4 -4,4\"/>\n",
68+
"<!-- node_0 -->\n",
69+
"<g id=\"node1\" class=\"node\">\n",
70+
"<title>node_0</title>\n",
71+
"<path fill=\"lightblue\" stroke=\"black\" d=\"M42,-36C42,-36 12,-36 12,-36 6,-36 0,-30 0,-24 0,-24 0,-12 0,-12 0,-6 6,0 12,0 12,0 42,0 42,0 48,0 54,-6 54,-12 54,-12 54,-24 54,-24 54,-30 48,-36 42,-36\"/>\n",
72+
"<text xml:space=\"preserve\" text-anchor=\"middle\" x=\"27\" y=\"-12.95\" font-family=\"Times,serif\" font-size=\"14.00\">5</text>\n",
73+
"</g>\n",
74+
"<!-- node_1 -->\n",
75+
"<g id=\"node2\" class=\"node\">\n",
76+
"<title>node_1</title>\n",
77+
"<path fill=\"lightblue\" stroke=\"black\" d=\"M132,-36C132,-36 102,-36 102,-36 96,-36 90,-30 90,-24 90,-24 90,-12 90,-12 90,-6 96,0 102,0 102,0 132,0 132,0 138,0 144,-6 144,-12 144,-12 144,-24 144,-24 144,-30 138,-36 132,-36\"/>\n",
78+
"<text xml:space=\"preserve\" text-anchor=\"middle\" x=\"117\" y=\"-12.95\" font-family=\"Times,serif\" font-size=\"14.00\">4</text>\n",
79+
"</g>\n",
80+
"<!-- node_0&#45;&gt;node_1 -->\n",
81+
"<g id=\"edge1\" class=\"edge\">\n",
82+
"<title>node_0&#45;&gt;node_1</title>\n",
83+
"<path fill=\"none\" stroke=\"black\" d=\"M54.4,-18C61.89,-18 70.18,-18 78.2,-18\"/>\n",
84+
"<polygon fill=\"black\" stroke=\"black\" points=\"78.1,-21.5 88.1,-18 78.1,-14.5 78.1,-21.5\"/>\n",
85+
"</g>\n",
86+
"<!-- node_2 -->\n",
87+
"<g id=\"node3\" class=\"node\">\n",
88+
"<title>node_2</title>\n",
89+
"<path fill=\"lightblue\" stroke=\"black\" d=\"M222,-36C222,-36 192,-36 192,-36 186,-36 180,-30 180,-24 180,-24 180,-12 180,-12 180,-6 186,0 192,0 192,0 222,0 222,0 228,0 234,-6 234,-12 234,-12 234,-24 234,-24 234,-30 228,-36 222,-36\"/>\n",
90+
"<text xml:space=\"preserve\" text-anchor=\"middle\" x=\"207\" y=\"-12.95\" font-family=\"Times,serif\" font-size=\"14.00\">3</text>\n",
91+
"</g>\n",
92+
"<!-- node_1&#45;&gt;node_2 -->\n",
93+
"<g id=\"edge2\" class=\"edge\">\n",
94+
"<title>node_1&#45;&gt;node_2</title>\n",
95+
"<path fill=\"none\" stroke=\"black\" d=\"M144.4,-18C151.89,-18 160.18,-18 168.2,-18\"/>\n",
96+
"<polygon fill=\"black\" stroke=\"black\" points=\"168.1,-21.5 178.1,-18 168.1,-14.5 168.1,-21.5\"/>\n",
97+
"</g>\n",
98+
"<!-- node_3 -->\n",
99+
"<g id=\"node4\" class=\"node\">\n",
100+
"<title>node_3</title>\n",
101+
"<path fill=\"lightblue\" stroke=\"black\" d=\"M312,-36C312,-36 282,-36 282,-36 276,-36 270,-30 270,-24 270,-24 270,-12 270,-12 270,-6 276,0 282,0 282,0 312,0 312,0 318,0 324,-6 324,-12 324,-12 324,-24 324,-24 324,-30 318,-36 312,-36\"/>\n",
102+
"<text xml:space=\"preserve\" text-anchor=\"middle\" x=\"297\" y=\"-12.95\" font-family=\"Times,serif\" font-size=\"14.00\">2</text>\n",
103+
"</g>\n",
104+
"<!-- node_2&#45;&gt;node_3 -->\n",
105+
"<g id=\"edge3\" class=\"edge\">\n",
106+
"<title>node_2&#45;&gt;node_3</title>\n",
107+
"<path fill=\"none\" stroke=\"black\" d=\"M234.4,-18C241.89,-18 250.18,-18 258.2,-18\"/>\n",
108+
"<polygon fill=\"black\" stroke=\"black\" points=\"258.1,-21.5 268.1,-18 258.1,-14.5 258.1,-21.5\"/>\n",
109+
"</g>\n",
110+
"<!-- node_4 -->\n",
111+
"<g id=\"node5\" class=\"node\">\n",
112+
"<title>node_4</title>\n",
113+
"<path fill=\"lightblue\" stroke=\"black\" d=\"M402,-36C402,-36 372,-36 372,-36 366,-36 360,-30 360,-24 360,-24 360,-12 360,-12 360,-6 366,0 372,0 372,0 402,0 402,0 408,0 414,-6 414,-12 414,-12 414,-24 414,-24 414,-30 408,-36 402,-36\"/>\n",
114+
"<text xml:space=\"preserve\" text-anchor=\"middle\" x=\"387\" y=\"-12.95\" font-family=\"Times,serif\" font-size=\"14.00\">1</text>\n",
115+
"</g>\n",
116+
"<!-- node_3&#45;&gt;node_4 -->\n",
117+
"<g id=\"edge4\" class=\"edge\">\n",
118+
"<title>node_3&#45;&gt;node_4</title>\n",
119+
"<path fill=\"none\" stroke=\"black\" d=\"M324.4,-18C331.89,-18 340.18,-18 348.2,-18\"/>\n",
120+
"<polygon fill=\"black\" stroke=\"black\" points=\"348.1,-21.5 358.1,-18 348.1,-14.5 348.1,-21.5\"/>\n",
121+
"</g>\n",
122+
"</g>\n",
123+
"</svg>\n"
124+
],
57125
"text/plain": [
58-
"[5, 4, 3, 2, 1]"
126+
"ListNode([5, 4, 3, 2, 1])"
59127
]
60128
},
61129
"execution_count": 3,
@@ -75,15 +143,7 @@
75143
"execution_count": 4,
76144
"id": "test",
77145
"metadata": {},
78-
"outputs": [
79-
{
80-
"name": "stdout",
81-
"output_type": "stream",
82-
"text": [
83-
"✅ Test passed! Linked list successfully reversed.\n"
84-
]
85-
}
86-
],
146+
"outputs": [],
87147
"source": [
88148
"# Verify the result matches expected output\n",
89149
"assert result == expected"

0 commit comments

Comments
 (0)