Skip to content
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ My LeetCode solutions in Java, focused on clean code and optimal algorithms.

## Solutions

| #* | Problem | Difficulty | Time | Space | [Blind 75][blind-75] |
|-----------------|----------------------------------------------------------------------------------------------------------------------------------|------------|-------------------|--------|----------------------|
| [1][lc-1] | [Two Sum](src/main/java/codes/yam/leetcode/twosum/Solution.java) | Easy | `O(n log n)` | `O(n)` | Yes |
| [9][lc-9] | [Palindrome Number](src/main/java/codes/yam/leetcode/palindromenumber/Solution.java) | Easy | `O(log10(n) / 2)` | `O(1)` | No |
| [70][lc-70] | [Climbing Stairs](src/main/java/codes/yam/leetcode/climbingstairs/Solution.java) | Easy | `O(n)` | `O(1)` | Yes |
| [746][lc-746] | [Min Cost Climbing Stairs](src/main/java/codes/yam/leetcode/mincostclimbingstairs/Solution.java) | Easy | `O(n)` | `O(1)` | No |
| [1653][lc-1653] | [Minimum Deletions to Make String Balanced](src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/Solution.java) | Medium | `O(n)` | `O(1)` | No |
| #* | Problem | Difficulty | Time | Space | [Blind 75][blind-75] |
|-----------------|----------------------------------------------------------------------------------------------------------------------------------|------------|-------------------|-------------|----------------------|
| [1][lc-1] | [Two Sum](src/main/java/codes/yam/leetcode/twosum/Solution.java) | Easy | `O(n log n)` | `O(n)` | Yes |
| [9][lc-9] | [Palindrome Number](src/main/java/codes/yam/leetcode/palindromenumber/Solution.java) | Easy | `O(log10(n) / 2)` | `O(1)` | No |
| [70][lc-70] | [Climbing Stairs](src/main/java/codes/yam/leetcode/climbingstairs/Solution.java) | Easy | `O(n)` | `O(1)` | Yes |
| [322][lc-322] | [Coin Change](src/main/java/codes/yam/leetcode/coinchange/Solution.java) | Medium | `O(amount × n)` | `O(amount)` | Yes |
| [746][lc-746] | [Min Cost Climbing Stairs](src/main/java/codes/yam/leetcode/mincostclimbingstairs/Solution.java) | Easy | `O(n)` | `O(1)` | No |
| [1653][lc-1653] | [Minimum Deletions to Make String Balanced](src/main/java/codes/yam/leetcode/minimumdeletionstomakestringbalanced/Solution.java) | Medium | `O(n)` | `O(1)` | No |

<sup>*Problem numbers link to LeetCode; problem names link to solution source.</sup>

Expand All @@ -22,6 +23,8 @@ My LeetCode solutions in Java, focused on clean code and optimal algorithms.

[lc-70]: https://leetcode.com/problems/climbing-stairs/

[lc-322]: https://leetcode.com/problems/coin-change/

[lc-746]: https://leetcode.com/problems/min-cost-climbing-stairs/

[lc-1653]: https://leetcode.com/problems/minimum-deletions-to-make-string-balanced/
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/codes/yam/leetcode/coinchange/Solution.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package codes.yam.leetcode.coinchange;

import java.util.Arrays;

/**
* Solution for the <b>Coin Change</b> problem.
*
* <ul>
* <li><b>Time Complexity:</b> <code>O(amount * n)</code> where {@code n} is {@code coins.length}
* <li><b>Space Complexity:</b> <code>O(amount)</code>
* </ul>
*/
class Solution {
int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
Arrays.fill(dp, 1, amount + 1, amount + 1);
for (int a = 1; a <= amount; a++) {
for (int c : coins) {
if (c <= a) {
dp[a] = Math.min(dp[a], 1 + dp[a - c]);
}
}
}
if (dp[amount] > amount) {
return -1;
}
return dp[amount];
}
}
37 changes: 37 additions & 0 deletions src/main/java/codes/yam/leetcode/coinchange/SolutionNaive.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package codes.yam.leetcode.coinchange;

/**
* Solution for the <b>Coin Change</b> problem.
*
* <p>Naive bottom-up DP using {@code -1} as the sentinel for unreachable states. See {@link
* Solution} for the simplified version using {@code amount + 1} as infinity.
*
* <ul>
* <li><b>Time Complexity:</b> <code>O(amount * n)</code> where {@code n} is {@code coins.length}
* <li><b>Space Complexity:</b> <code>O(amount)</code>
* </ul>
*/
class SolutionNaive {
int coinChange(int[] coins, int amount) {
if (amount == 0) return 0;
int[] dp = new int[amount + 1];
for (int a = 1; a <= amount; a++) {
boolean useMin = true;
int min = Integer.MAX_VALUE;
for (int c : coins) {
if (c > a) continue;
if (c == a) {
dp[a] = 1;
useMin = false;
continue;
}
if (dp[a - c] == -1) continue;
min = Math.min(min, 1 + dp[a - c]);
}
if (useMin) {
dp[a] = min == Integer.MAX_VALUE ? -1 : min;
}
}
return dp[amount];
}
}
22 changes: 22 additions & 0 deletions src/main/java/codes/yam/leetcode/coinchange/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Solutions for the "Coin Change" problem on LeetCode.
*
* <ul>
* <li><b>Slug:</b> <code>coin-change</code>
* <li><b>Difficulty:</b> Medium
* </ul>
*
* <p><b>Solution progression:</b>
*
* <ol>
* <li>{@link codes.yam.leetcode.coinchange.SolutionNaive} — bottom-up DP using {@code -1} as the
* sentinel for unreachable states. Requires special-casing {@code c == amount} and a {@code
* useMin} flag to avoid treating unset cells as valid.
* <li>{@link codes.yam.leetcode.coinchange.Solution} — same bottom-up DP, but uses {@code amount
* + 1} as the infinity sentinel. Since {@code ∞ + 1 = ∞} and {@code min(∞, x) = x}, the
* sentinel is algebraically compatible with the recurrence and all special cases collapse.
* </ol>
*
* @see <a href="https://leetcode.com/problems/coin-change/">Problem Link</a>
*/
package codes.yam.leetcode.coinchange;
14 changes: 14 additions & 0 deletions src/test/java/codes/yam/leetcode/coinchange/SolutionNaiveTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package codes.yam.leetcode.coinchange;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

class SolutionNaiveTest {
@ParameterizedTest
@MethodSource("codes.yam.leetcode.coinchange.TestCases#cases")
void coinChange(int[] coins, int amount, int expected) {
assertEquals(expected, new SolutionNaive().coinChange(coins, amount));
}
}
14 changes: 14 additions & 0 deletions src/test/java/codes/yam/leetcode/coinchange/SolutionTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package codes.yam.leetcode.coinchange;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

class SolutionTest {
@ParameterizedTest
@MethodSource("codes.yam.leetcode.coinchange.TestCases#cases")
void coinChange(int[] coins, int amount, int expected) {
assertEquals(expected, new Solution().coinChange(coins, amount));
}
}
14 changes: 14 additions & 0 deletions src/test/java/codes/yam/leetcode/coinchange/TestCases.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package codes.yam.leetcode.coinchange;

import java.util.stream.Stream;
import org.junit.jupiter.params.provider.Arguments;

@SuppressWarnings("unused")
class TestCases {
static Stream<Arguments> cases() {
return Stream.of(
Arguments.of(new int[] {1, 2, 5}, 11, 3),
Arguments.of(new int[] {2}, 3, -1),
Arguments.of(new int[] {1}, 0, 0));
}
}