Skip to content

Commit

Permalink
update post
Browse files Browse the repository at this point in the history
  • Loading branch information
dev-jonghoonpark committed Jul 9, 2024
1 parent 605857f commit f32cb1b
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 5 deletions.
130 changes: 125 additions & 5 deletions _posts/2024-02-26-leetcode-322.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
layout: post
title: (Leetcode) 322 - Coin Change
title: (Leetcode) 322 - Coin Change 풀이
categories: [스터디-알고리즘]
tags: [파이썬, 알고리즘, python, algorithm, Leetcode, DP, coin]
tags: [파이썬, 알고리즘, python, algorithm, Leetcode, DP, coin, java, stream]
date: 2024-02-26 22:00:00 +0900
image:
path: /assets/images/2024-02-26-leetcode-322/page1.png
Expand All @@ -22,7 +22,7 @@ DP 문제이다.

### 처음 생각

처음에는 큰 동전부터 넣어서 될 때까지 하면 베스트 일 것이라고 생각했다. 하지만 그렇게 간단하지는 않았다.
처음에는 큰 동전부터 넣어서 될 때까지 하면 베스트 일 것이라고 생각했다. 하지만 그렇게 간단하지는 않았다.

예를들어 `[100, 30, 1]` 이라는 동전이 있다고 해보자.
이때 주어지는 amount는 120 이라고 하면
Expand All @@ -31,7 +31,7 @@ DP 문제이다.

### 중간 생각

그래서 모든 경우의 수를 다 찾아봐야 겠다고 생각했다.
그래서 모든 경우의 수를 다 찾아봐야 겠다고 생각했다.
처음에는 잘 먹히는 듯 보였으나, 역시나 복잡한 경우로 인해 타임아웃이 발생되었다.

```python
Expand Down Expand Up @@ -129,17 +129,137 @@ print(solution.coinChange([1, 2, 5], 10))
![example page5](/assets/images/2024-02-26-leetcode-322/page5.png)

### 시간복잡도

- 이중 반복문 사용
- 바깥쪽 반복문 : amount 만큼 반복 (O(n))
- 안쪽 반복문 : coins 리스트의 length 만큼 반복 (O(m))

`O(n * m)` 의 시간 복잡도를 가짐

### 공간복잡도

amount 만큼 dp array를 생성함

`O(n)` 의 공간 복잡도를 가짐

## 결론

DP 문제를 더 많이 접해봐야겠다는 생각이 들었다.
DP 문제를 더 많이 접해봐야겠다는 생각이 들었다.

## java 로 다시 풀기 (24.07.09)

먼저는 stream으로 풀었다. 이미 들린 dp pointer 일 때, 기존에 등록된 값보다 클 경우 (`dp[currentPointer] > dp[amount] + 1`) 이후 dfs 단계를 생략하도록 한 것이 포인트이다.

처음으로 `dp[0]`에 도달했다고 해서 최소값이 아니기 때문에 전체 케이스를 고려해야 하는데 그렇다고 진짜로 전체 케이스를 확인해본다면 timeout이 발생된다. 따라서 이 조건문을 찾아내지 못하면 timeout이 발생된다.

```java
class Solution {
public int coinChange(int[] coins, int amount) {
if(amount == 0) {
return 0;
}

int[] dp = new int[amount + 1];

List<Integer> sortedCoins = Arrays.stream(coins).boxed()
.sorted(Collections.reverseOrder())
.toList();

sortedCoins.forEach(coin -> dfs(dp, sortedCoins, amount, coin));

return dp[0] == 0 ? -1 : dp[0];
}

void dfs(int[] dp, List<Integer> coins, int amount, int selectedCoin) {
int currentPointer = amount - selectedCoin;
if (currentPointer < 0) {
return;
}

if (dp[currentPointer] == 0 || dp[currentPointer] > dp[amount] + 1) {
dp[currentPointer] = dp[amount] + 1;
coins.forEach(coin -> dfs(dp, coins, currentPointer, coin));
}
}
}
```

stream 의 경우 for loop 보다는 느리지만 이해하기에는 훨씬 좋다. 더 직관적이다.

![java-using-for-loop](/assets/images/2024-02-26-leetcode-322/java-using-for-loop.png)

다만 성능을 더 최적화 하기 위해서 for loop를 이용해서 풀도록 바꾸면 다음과 같다.

```java
class Solution {
public int coinChange(int[] coins, int amount) {
if(amount == 0) {
return 0;
}

int[] dp = new int[amount + 1];

Arrays.sort(coins);
for (int i = coins.length - 1; i > -1; i--) {
dfs(dp, coins, amount, coins[i]);
}

return dp[0] == 0 ? -1 : dp[0];
}

void dfs(int[] dp, int[] coins, int amount, int selectedCoin) {
int currentPointer = amount - selectedCoin;
if (currentPointer < 0) {
return;
}

if (dp[currentPointer] == 0 || dp[currentPointer] > dp[amount] + 1) {
dp[currentPointer] = dp[amount] + 1;
for (int i = coins.length - 1; i > -1; i--) {
dfs(dp, coins, currentPointer, coins[i]);
}
}
}
}
```

![java-using-for-loop](/assets/images/2024-02-26-leetcode-322/java-using-for-loop.png)

stream을 사용했을 때보다 시간이 단축된 것을 볼 수 있다.

### TS, SC

코인의 수를 n 이라고 했을 때, `O(n * amount ^ 2)` 의 시간복잡도와 `O(amount)` 의 공간복잡도를 가진다.

## java 모범 답안 (더 효율적인 방법)

위 코드는 dp 라는 array를 사용하긴 했지만, 사실 brute force 에 가까운 방법이다.

아래와 같이 작성하면 더 효율적으로 동작한다.

```java
public class Solution {
public int coinChange(int[] coins, int amount) {
int max = amount + 1;
int[] dp = new int[amount + 1];
Arrays.fill(dp, max);
dp[0] = 0;

for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.length; j++) {
if (coins[j] <= i) {
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
}
}
}

return dp[amount] > amount ? -1 : dp[amount];
}
}
```

### TS, SC

코인의 수를 n 이라고 했을 때, `O(n * amount)` 의 시간복잡도와 `O(amount)` 의 공간복잡도를 가진다.

![java-using-dp](/assets/images/2024-02-26-leetcode-322/java-using-dp.png)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit f32cb1b

Please sign in to comment.