From 67538e023781462c4af367cab06cfd867a4aec4c Mon Sep 17 00:00:00 2001 From: Lusina <12752833+BrianLusina@users.noreply.github.com> Date: Tue, 9 Sep 2025 10:50:26 +0300 Subject: [PATCH 1/3] feat(algorithms, arrays): non constructible change --- .../arrays/non_constructible_change/README.md | 20 ++++ .../non_constructible_change/__init__.py | 17 ++++ .../test_non_constructible_change.py | 99 +++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 algorithms/arrays/non_constructible_change/README.md create mode 100644 algorithms/arrays/non_constructible_change/__init__.py create mode 100644 algorithms/arrays/non_constructible_change/test_non_constructible_change.py diff --git a/algorithms/arrays/non_constructible_change/README.md b/algorithms/arrays/non_constructible_change/README.md new file mode 100644 index 00000000..507039fc --- /dev/null +++ b/algorithms/arrays/non_constructible_change/README.md @@ -0,0 +1,20 @@ +# Non-Constructible Change + +Given an array of positive integers representing the values of coins in your +possession, write a function that returns the minimum amount of change (the +minimum sum of money) that you cannot create. The given coins can have +any positive integer value and aren't necessarily unique (i.e., you can have +multiple coins of the same value). + + +Sample Input: + +```plain +coins = [5,7,1,1,2,3,22] +``` + +Sample output: + +```plain +20 +``` diff --git a/algorithms/arrays/non_constructible_change/__init__.py b/algorithms/arrays/non_constructible_change/__init__.py new file mode 100644 index 00000000..7da19ea5 --- /dev/null +++ b/algorithms/arrays/non_constructible_change/__init__.py @@ -0,0 +1,17 @@ +from typing import List + + +def non_constructible_change(coins: List[int]) -> int: + """ + + """ + coins.sort() + + current_change_created = 0 + + for coin in coins: + if coin > current_change_created + 1: + return current_change_created + 1 + current_change_created += coin + + return current_change_created + 1 diff --git a/algorithms/arrays/non_constructible_change/test_non_constructible_change.py b/algorithms/arrays/non_constructible_change/test_non_constructible_change.py new file mode 100644 index 00000000..6103dc89 --- /dev/null +++ b/algorithms/arrays/non_constructible_change/test_non_constructible_change.py @@ -0,0 +1,99 @@ +import unittest +from . import non_constructible_change + + +class NonConstructibleChangeTestCase(unittest.TestCase): + def test_1(self): + """should return 20 for input of [5, 7, 1, 1, 2, 3, 22]""" + coins = [5, 7, 1, 1, 2, 3, 22] + expected = 20 + actual = non_constructible_change(coins) + self.assertEqual(actual, expected) + + def test_2(self): + """should return 6 for input of [1, 1, 1, 1, 1]""" + coins = [1, 1, 1, 1, 1] + expected = 6 + actual = non_constructible_change(coins) + self.assertEqual(actual, expected) + + def test_3(self): + """should return 55 for input of [1, 5, 1, 1, 1, 10, 15, 20, 100]""" + coins = [1, 5, 1, 1, 1, 10, 15, 20, 100] + expected = 55 + actual = non_constructible_change(coins) + self.assertEqual(actual, expected) + + def test_4(self): + """should return 3 for input of [6, 4, 5, 1, 1, 8, 9]""" + coins = [6, 4, 5, 1, 1, 8, 9] + expected = 3 + actual = non_constructible_change(coins) + self.assertEqual(actual, expected) + + def test_5(self): + """should return 1 for input of []""" + coins = [] + expected = 1 + actual = non_constructible_change(coins) + self.assertEqual(actual, expected) + + def test_6(self): + """should return 1 for input of [87]""" + coins = [87] + expected = 1 + actual = non_constructible_change(coins) + self.assertEqual(actual, expected) + + def test_7(self): + """should return 32 for input of [5, 6, 1, 1, 2, 3, 4, 9]""" + coins = [5, 6, 1, 1, 2, 3, 4, 9] + expected = 32 + actual = non_constructible_change(coins) + self.assertEqual(actual, expected) + + def test_8(self): + """should return 19 for input of [5, 6, 1, 1, 2, 3, 43]""" + coins = [5, 6, 1, 1, 2, 3, 43] + expected = 19 + actual = non_constructible_change(coins) + self.assertEqual(actual, expected) + + def test_9(self): + """should return 3 for input of [1, 1]""" + coins = [1, 1] + expected = 3 + actual = non_constructible_change(coins) + self.assertEqual(actual, expected) + + def test_10(self): + """should return 1 for input of [2]""" + coins = [2] + expected = 1 + actual = non_constructible_change(coins) + self.assertEqual(actual, expected) + + def test_11(self): + """should return 2 for input of [1]""" + coins = [1] + expected = 2 + actual = non_constructible_change(coins) + self.assertEqual(actual, expected) + + def test_12(self): + """should return 87 for input of [109, 2000, 8765, 19, 18, 17, 16, 8, 1, 1, 2, 4]""" + coins = [109, 2000, 8765, 19, 18, 17, 16, 8, 1, 1, 2, 4] + expected = 87 + actual = non_constructible_change(coins) + self.assertEqual(actual, expected) + + def test_13(self): + """should return 29 for input of [1, 2, 3, 4, 5, 6, 7]""" + coins = [1, 2, 3, 4, 5, 6, 7] + expected = 29 + actual = non_constructible_change(coins) + self.assertEqual(actual, expected) + + +if __name__ == '__main__': + unittest.main() From ddd9989e174b22df4f3ca3b82395371ac909b318 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Tue, 9 Sep 2025 07:50:45 +0000 Subject: [PATCH 2/3] updating DIRECTORY.md --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index fa888559..032c1824 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -9,6 +9,8 @@ * [Test Intersection Two](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/arrays/intersection/test_intersection_two.py) * Majority Element * [Test Majority Element](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/arrays/majority_element/test_majority_element.py) + * Non Constructible Change + * [Test Non Constructible Change](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/arrays/non_constructible_change/test_non_constructible_change.py) * Optimal Task Assignment * [Test Optimal Task Assignment](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/arrays/optimal_task_assignment/test_optimal_task_assignment.py) * Remove Duplicates From 85497130a6447e6178da5b0dfdc43e743287e49b Mon Sep 17 00:00:00 2001 From: Lusina <12752833+BrianLusina@users.noreply.github.com> Date: Tue, 9 Sep 2025 11:03:38 +0300 Subject: [PATCH 3/3] refactor(algorithms, arrays): remove sorting in place --- .../arrays/non_constructible_change/README.md | 4 ++-- .../arrays/non_constructible_change/__init__.py | 15 +++++++++++++-- .../test_non_constructible_change.py | 6 ++++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/algorithms/arrays/non_constructible_change/README.md b/algorithms/arrays/non_constructible_change/README.md index 507039fc..87214c69 100644 --- a/algorithms/arrays/non_constructible_change/README.md +++ b/algorithms/arrays/non_constructible_change/README.md @@ -9,12 +9,12 @@ multiple coins of the same value). Sample Input: -```plain +```text coins = [5,7,1,1,2,3,22] ``` Sample output: -```plain +```text 20 ``` diff --git a/algorithms/arrays/non_constructible_change/__init__.py b/algorithms/arrays/non_constructible_change/__init__.py index 7da19ea5..949ee7b3 100644 --- a/algorithms/arrays/non_constructible_change/__init__.py +++ b/algorithms/arrays/non_constructible_change/__init__.py @@ -3,13 +3,24 @@ def non_constructible_change(coins: List[int]) -> int: """ + Return the smallest amount of change that cannot be created from the given coins. + Greedy approach: track the maximum constructible change 'current_change_created'. + If the next coin is greater than current_change_created + 1, we've found the gap. + Examples: + >>> non_constructible_change([5, 7, 1, 1, 2, 3, 22]) + 20 + >>> non_constructible_change([]) + 1 + >>> non_constructible_change([1, 1, 1, 1, 1]) + 6 """ - coins.sort() + # Do not mutate the caller's input; iterate over a sorted copy. + sorted_coins = sorted(coins) current_change_created = 0 - for coin in coins: + for coin in sorted_coins: if coin > current_change_created + 1: return current_change_created + 1 current_change_created += coin diff --git a/algorithms/arrays/non_constructible_change/test_non_constructible_change.py b/algorithms/arrays/non_constructible_change/test_non_constructible_change.py index 6103dc89..0963b12c 100644 --- a/algorithms/arrays/non_constructible_change/test_non_constructible_change.py +++ b/algorithms/arrays/non_constructible_change/test_non_constructible_change.py @@ -94,6 +94,12 @@ def test_13(self): actual = non_constructible_change(coins) self.assertEqual(actual, expected) + def test_input_not_mutated(self): + """should not mutate the input coins list""" + coins = [5, 7, 1, 1, 2, 3, 22] + snapshot = coins[:] + _ = non_constructible_change(coins) + self.assertEqual(coins, snapshot) if __name__ == '__main__': unittest.main()