Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions coin-change/unpo88.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
class Solution:
def coinChange(self, coins: list[int], amount: int) -> int:
dp = [float('inf')] * (amount + 1)
dp[0] = 0

for coin in coins:
for x in range(coin, amount + 1):
dp[x] = min(dp[x], dp[x - coin] + 1)

return dp[amount] if dp[amount] != float('inf') else -1

"""
================================================================================
풀이 과정
================================================================================

[1차 시도] 완전 탐색으로 접근하면?
────────────────────────────────────────────────────────────────────────────────
1. 모든 동전 조합을 시도해보면 어떨까?
2. coins = [1, 2, 5], amount = 11
- 5 + 5 + 1 = 11 (3개)
- 5 + 2 + 2 + 2 = 11 (4개)
- 1 + 1 + ... (11개)
... 너무 많은 경우의 수!

3. 문제: 시간복잡도가 너무 높음 (지수 시간)
4. 더 효율적인 방법이 필요함 → DP로 접근하자!

────────────────────────────────────────────────────────────────────────────────
[2차 시도] DP 초기화와 점화식
────────────────────────────────────────────────────────────────────────────────
5. dp[i] = i원을 만드는데 필요한 최소 동전 개수
6. 초기화:
- dp[0] = 0 (0원 만들기 = 동전 0개)
- dp[1~amount] = 아직 계산 안 됨

dp = [float('inf')] * (amount + 1)
dp[0] = 0

7. 점화식:
- 각 동전 coin에 대해
- dp[x] = min(dp[x], dp[x - coin] + 1)
- 의미: "x원 = (x-coin)원 + coin 1개"

8. Eample) coins = [1, 2, 5], amount = 11

초기: dp = [0, inf, inf, inf, ..., inf]

동전 1 처리:
dp[1] = min(inf, dp[0]+1) = 1
dp[2] = min(inf, dp[1]+1) = 2
dp[3] = min(inf, dp[2]+1) = 3
...

동전 2 처리:
dp[2] = min(2, dp[0]+1) = 1 # 2원 동전 1개!
dp[3] = min(3, dp[1]+1) = 2 # 2+1
dp[4] = min(4, dp[2]+1) = 2 # 2+2
...

동전 5 처리:
dp[5] = min(5, dp[0]+1) = 1 # 5원 동전 1개!
dp[6] = min(6, dp[1]+1) = 2 # 5+1
dp[10] = min(10, dp[5]+1) = 2 # 5+5
dp[11] = min(11, dp[6]+1) = 3 # 5+5+1


[최종 구현] Bottom-Up DP
────────────────────────────────────────────────────────────────────────────────
12. 모든 동전에 대해 반복
13. 각 동전으로 만들 수 있는 모든 금액 업데이트
14. 불가능하면 -1 반환 (dp[amount]가 여전히 무한대)

for coin in coins:
for x in range(coin, amount + 1):
dp[x] = min(dp[x], dp[x - coin] + 1)

return dp[amount] if dp[amount] != float('inf') else -1

15. 시간복잡도: O(amount × coins) - 효율적!
16. 공간복잡도: O(amount) - dp 배열
"""
38 changes: 38 additions & 0 deletions find-minimum-in-rotated-sorted-array/unpo88.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
class Solution:
def findMin(self, nums: List[int]) -> int:
left, right = 0, len(nums) - 1

while left < right:
mid = (left + right) // 2

if nums[mid] > nums[right]:
left = mid + 1
else:
right = mid

return nums[left]

"""
================================================================================
풀이 과정
================================================================================

[1차 시도] 이진 탐색 적용 - 기본 구조
────────────────────────────────────────────────────────────────────────────────
1. log(n) 시간 복잡도를 만족시키는 이진 탐색 구조로 최소값을 찾으면 될 것 같음

left, right = 0, len(nums) - 1

while left < right:
mid = (left + right) // 2

if nums[mid] > nums[right]:
left = mid + 1
else:
right = mid

return nums[left]

2. 시간복잡도: O(log n) - 이진 탐색
3. 공간복잡도: O(1) - 추가 공간 사용 안 함
"""
52 changes: 52 additions & 0 deletions maximum-depth-of-binary-tree/unpo88.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root:
return 0

left_depth = self.maxDepth(root.left)
right_depth = self.maxDepth(root.right)

return max(left_depth, right_depth) + 1

"""
================================================================================
풀이 과정
================================================================================

[1차 시도] 재귀로 깊이 카운트 - None은 0
────────────────────────────────────────────────────────────────────────────────
1. 빈 노드(None)는 깊이가 0이
2. leaf 노드에서 양쪽 자식이 None이면 둘 다 0을 반환
3. 그러면 leaf 노드는 max(0, 0) + 1 = 1이 됨 (자기 자신만 카운트)

def maxDepth(self, root):
if not root:
return 0 # 빈 노드는 0

left = self.maxDepth(root.left)
right = self.maxDepth(root.right)

return max(left, right) + 1 # 더 깊은 쪽 + 나 자신(1)

4. 동작 예시:
트리: 1
/ \
2 3
/
4

maxDepth(4) → max(0, 0) + 1 = 1
maxDepth(2) → max(1, 0) + 1 = 2
maxDepth(3) → max(0, 0) + 1 = 1
maxDepth(1) → max(2, 1) + 1 = 3 ✓


5. 시간복잡도: O(n) - 모든 노드를 1번씩 방문
6. 공간복잡도: O(h) - 재귀 스택, h는 트리 높이
"""
55 changes: 55 additions & 0 deletions merge-two-sorted-lists/unpo88.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode()
head = dummy

while list1 and list2:
if list1.val < list2.val:
head.next = list1
list1 = list1.next
else:
head.next = list2
list2 = list2.next
head = head.next

if list1:
head.next = list1
elif list2:
head.next = list2

return dummy.next

"""
================================================================================
풀이 과정
================================================================================

[1차 시도] Dummy Node 활용
────────────────────────────────────────────────────────────────────────────────
1. 작은 값을 가진 노드를 골라서 head에 연결하는 방식으로 문제를 풀어보자.
2. 한 쪽 리스트가 비게 되면 남은 노드를 그냥 연결해주면 될 것 같음.

dummy = ListNode()
head = dummy

while list1 and list2:
if list1.val < list2.val:
head.next = list1
list1 = list1.next
else:
head.next = list2
list2 = list2.next
head = head.next

if list1:
head.next = list1
elif list2:
head.next = list2

return dummy.next
"""
111 changes: 111 additions & 0 deletions word-search/unpo88.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
class Solution:
def exist(self, board: list[list[str]], word: str) -> bool:
if not board or not board[0]:
return False

m, n = len(board), len(board[0])

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]

def dfs(x, y, index):
if index == len(word):
return True

if x < 0 or x >= m or y < 0 or y >= n or board[x][y] != word[index]:
return False

temp = board[x][y]
board[x][y] = '#'

for i in range(4):
nx = x + dx[i]
ny = y + dy[i]
if dfs(nx, ny, index + 1):
return True

board[x][y] = temp
return False

for i in range(m):
for j in range(n):
if board[i][j] == word[0] and dfs(i, j, 0):
return True

return False

"""
================================================================================
풀이 과정
================================================================================

[1차 시도] DFS + 방향 배열로 접근
────────────────────────────────────────────────────────────────────────────────
1. 격자에서 상하좌우로 이동하며 단어를 찾아야 함
2. 같은 셀은 한 번만 사용 가능 → 방문 체크 필요
3. DFS(깊이 우선 탐색) + 백트래킹으로 풀면 될 것 같음
4. 상하좌우 이동을 위한 dx, dy 배열 만들자

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]


[2차 시도] DFS 함수 구조 설계
────────────────────────────────────────────────────────────────────────────────
5. dfs(x, y, index) 형태로 현재 위치와 단어의 인덱스를 추적
6. base case:
- index == len(word): 단어 끝까지 찾음 → True
- 범위 벗어남 or 문자 불일치 → False

def dfs(x, y, index):
if index == len(word):
return True

if x < 0 or x >= m or y < 0 or y >= n or board[x][y] != word[index]:
return False

7. 방문한 셀은 어떻게 표시하지?


[3차 시도] 방문 표시와 백트래킹
────────────────────────────────────────────────────────────────────────────────
8. 현재 셀을 '#' 같은 특수 문자로 임시 변경 (방문 표시)
9. 4방향으로 재귀 탐색
10. 탐색 실패 시 원래 값으로 복원 (백트래킹)

temp = board[x][y]
board[x][y] = '#'

for i in range(4):
nx = x + dx[i]
ny = y + dy[i]
if dfs(nx, ny, index + 1):
return True

board[x][y] = temp
return False


[4차 시도] 조기 종료 최적화 추가
────────────────────────────────────────────────────────────────────────────────
11. 빈 보드는 바로 False 반환
12. 첫 글자가 일치하는 셀에서만 DFS 시작 (불필요한 탐색 방지)

if not board or not board[0]:
return False

for i in range(m):
for j in range(n):
if board[i][j] == word[0] and dfs(i, j, 0):
return True


[최종 구현] 최적화된 DFS 탐색
────────────────────────────────────────────────────────────────────────────────
13. 조기 종료로 불필요한 탐색 제거
14. 백트래킹으로 방문 상태 관리
15. 하나라도 성공하면 즉시 True 반환

16. 시간복잡도: O(m * n * 4^L) - 최악의 경우, 조기 종료로 실제로는 더 빠름
17. 공간복잡도: O(L) - 재귀 깊이
"""
Loading