Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
24 changes: 10 additions & 14 deletions coin-change/okyungjin.py
Comment thread
okyungjin marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
# coins로 amount를 만들 수 없는 경우 -1을 반환
# 동전의 종류는 무한대라고 가정하고 문제를 풀 것

# 처음에는 greedy로 접근하려고 했는데, 금액이 큰 동전을 먼저 선택하는게 최소 동전의 수가 아니라서 dp로 풀이했다.

# [복잡도]
# C: 코인의 개수, # A: amount
# 시간 복잡도: O(A * C)
Expand All @@ -19,21 +17,19 @@ def coinChange(self, coins: List[int], amount: int) -> int:
# min_coins[i]: i원을 만드는데 필요한 최소 동전의 개수
min_coins = [NOT_POSSIBLE] * (amount + 1)
min_coins[0] = 0

# 최적화를 위해 정렬
coins.sort()

# 1부터 목표 금액(amount)까지 상향식으로 최소 동전 개수를 계산
for cur_amount in range(1, amount + 1):
# coin: 선택한 동전의 금액
# 모든 동전 종류를 확인하여 현재 금액을 만들 수 있는 조합 탐색
for coin in coins:
# coins가 정렬되어 있으므로, cur_amount 보다 동전의 금액이 더 크면 이후는 확인 불필요
if coin > cur_amount:
break

# 둘 중 더 작은 값으로 업데이트
# 1. 기존에 구한 개수
# 2. 현재 동전을 1개 추가해서 만드는 개수
min_coins[cur_amount] = min(min_coins[cur_amount], min_coins[cur_amount - coin] + 1)
# 동전 금액이 현재 금액보다 작거나 같을 때만 유효한 조합으로 간주
if coin <= cur_amount:
# '남은 금액을 만드는 최소 개수' + '현재 동전 1개'를 합산
Comment on lines +23 to +27

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if coin <= cur_amount 로직을 포문 안에서 계속 돌게 해주신 이유가 있을까요?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

원래는 coins를 정렬한 뒤 if coin > cur_amount: break 조기 종료 조건을 사용했었는데요. 코드를 더 간결하게 다듬기 위해 정렬을 없애고 부등호를 뒤집는 방향으로 수정했습니다.

coins 배열 길이가 최대 12라 break 유무에 따른 성능 차이가 크지 않을 것 같아 로직을 간소화해 보았습니다!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사담이지만 그 아래 라인에 있는 min 함수를 제거하니 성능이 훨씬 좋아지더라구요

min_coins[cur_amount] = min(min_coins[cur_amount], min_coins[cur_amount - coin] + 1)

if문 형태로 필요할 때만 할당하도록 하니 런타임 백분위가 30% 올라갔던 것 같습니다.

@alphaorderly alphaorderly Jul 19, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞아요, max나 min으로 하는게 코드상 보기 좋고 쓰기도 편한게 맞는데
성능은 은근히 차이가 나더라구요
몇몇 문제는 100~200ms 가량 차이나는것도 본적 있어요

new_val = min_coins[cur_amount - coin] + 1

# 더 적은 개수의 동전 조합을 찾았다면 해당 금액의 최솟값을 갱신
if new_val < min_coins[cur_amount]:
min_coins[cur_amount] = new_val

# 초기화된 값 그대로이면 불가능한 조합이므로 -1 반환
if min_coins[amount] == NOT_POSSIBLE:
Expand Down
6 changes: 4 additions & 2 deletions find-minimum-in-rotated-sorted-array/okyungjin.py
Comment thread
okyungjin marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
# 공간 복잡도: O(1)
class Solution:
def findMin(self, nums: List[int]) -> int:

# 왼쪽/오른쪽 값의 인덱스 계산
left = 0
right = len(nums) - 1

# 이미 정렬된 상태인 경우 or n번 회전한 경우 분기 처리
if nums[left] <= nums[right]:
return nums[left]

while left < right:
# 중간값 인덱스
mid = (left + right) // 2
Expand Down
71 changes: 62 additions & 9 deletions maximum-depth-of-binary-tree/okyungjin.py
Comment thread
okyungjin marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,28 +1,81 @@
# 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

# 시간 복잡도: O(N), 모든 노드를 한번 씩 방문
# 공간 복잡도: O(N), 최악의 경우 모든 노드가 큐에 담길 수 있음
'''
스택을 활용한 DFS

시간 복잡도: O(N)
공간 복잡도: O(H), H: 트리의 높이
'''
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
# 트리가 비어있으면 길이는 0
if root is None:
return 0

max_depth = 1

# 스택에 (현재 노드, 노드의 깊이) 튜플을 저장
stack = [(root, 1)]

while stack:
node, cur_depth = stack.pop()

# 현재 노드의 깊이와 저장된 최댓값을 비교하여 갱신
max_depth = max(cur_depth, max_depth)

# 왼쪽 자식 노드가 있다면 현재 깊이 + 1을 하여 스택에 추가
if node.left:
stack.append((node.left, cur_depth + 1))

# 오른쪽 자식 노드가 있다면 현재 깊이 + 1을 하여 스택에 추가
if node.right:
stack.append((node.right, cur_depth + 1))
Comment on lines +9 to 30

@parkhojeong parkhojeong Jul 19, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다른 파일들 포함해서 주석을 많이 달아주셨는데 코드만으로 충분한 것들이 많은 것 같아요!

코드를 통해서 최대한 의도를 드러내고 꼭 필요한 상황에만 적어주시는 편이 좋을 거 같아요. 주석도 같이 읽어야 해서 인지적 부하도 올라가고 가독성이 떨어지는 부분도 생기기도 해서요. 특히 코드와 주석이 정확히 일치하지 않는 경우가 생기지 않도록 조심해야 하는 거 같아요.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

의견 감사합니다!


return max_depth

'''
큐를 활용한 BFS

시간 복잡도: O(N)
공간 복잡도: O(W), W: 트리의 최대 너비
'''
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
# 트리가 비어있으면 길이는 0
if root is None:
return 0

queue = deque([root])
depth = 0

while queue:
# 현재 레벨에 있는 노드의 개수만큼 반복 처리
level_size = len(queue)

for _ in range(level_size):
node = queue.popleft()

if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)

# 한 레벨의 탐색이 끝나면 깊이 증가
depth += 1

return depth
Comment thread
okyungjin marked this conversation as resolved.

'''
재귀 함수 풀이

시간 복잡도: O(N)
공간 복잡도: O(H), H: 스택의 높이
'''
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if root is None:
return 0

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

# 현재 자신의 노드 하나를 더해서 최대 깊이를 반환
return max(left_depth, right_depth) + 1
114 changes: 16 additions & 98 deletions merge-two-sorted-lists/okyungjin.py
Comment thread
okyungjin marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,110 +1,28 @@
# from typing import Optional, List

# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next

# # 파이썬의 toString 역할을 하는 __repr__을 [1,3,4] 형태로 출력되게 수정
# def __repr__(self):
# nodes = []
# curr = self
# while curr:
# nodes.append(str(curr.val))
# curr = curr.next
# return "[" + ",".join(nodes) + "]"

# # 파이썬 list를 ListNode 리스트로 변환해주는 헬퍼 함수
# def make_linked_list(arr: List[int]) -> Optional[ListNode]:
# if not arr:
# return None
# dummy = ListNode(0)
# curr = dummy
# for val in arr:
# curr.next = ListNode(val)
# curr = curr.next
# return dummy.next


# list1의 노드 개수를 n, list2의 노드 개수를 m이라 할 때
# 시간 복잡도: O(n + m)
# 공간 복잡도: O(n + m)
class Solution_01:
# 공간 복잡도: O(1) -> dummy만 사용, 추가 공간 복잡도는 O(1)
class Solution:
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
# 맨 앞에 더미 노드를 하나 추가한다
# 없는 상태로 짰는데 if node: else: 구문 늘어나서 맨 앞에 가상의 노드를 하나 추가해줬다
# dummy: 병합된 결과 리스트의 시작점을 보존하기 위한 가상의 헤드 노드
# current: 현재 병합 위치를 추적하며 노드를 연결해 나가는 포인터
dummy = ListNode(None)
node = dummy
current = dummy

while True:
# list1이 비어있으면 현재 노드 뒤로 list2를 이어준다.
if not list1:
node.next = list2
break

# list2가 비어있으면 현재 노드 뒤로 list1을 이어준다.
if not list2:
node.next = list1
break

# 값을 비교해서 작은 값을 next 노드에 추가한다.
# 두 리스트가 모두 존재하는 동안 값을 비교하며 merge한다
while list1 and list2:
# 더 작은 값을 가진 노드를 결과 리스트에 연결
if list1.val <= list2.val:
node.next = ListNode(list1.val)
# list1 하나 소비
current.next = list1
list1 = list1.next
else:
node.next = ListNode(list2.val)
# list2 하나 소비
current.next = list2
list2 = list2.next

# 다음 노드를 현재 노드로 할당해준다
node = node.next

return dummy.next


# [접근법] Solution_01 의 공간 복잡도를 개선했습니다.
# ListNode를 새로 생성하지 않고 기존 노드를 활용하도록 수정했습니다.

# list1의 노드 개수를 n, list2의 노드 개수를 m이라 할 때
# 시간 복잡도: O(n + m)
# 공간 복잡도: O(1) -> dummy만 사용, 추가 공간 복잡도는 O(1)
class Solution:
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
# 맨 앞에 더미 노드를 하나 추가한다
# 없는 상태로 짰는데 if node: else: 구문 늘어나서 맨 앞에 가상의 노드를 하나 추가해줬다
dummy = ListNode(None)
node = dummy
# 병합 리스트의 포인터를 다음으로 이동
current = current.next

while True:
if not list1: # list1이 비어있으면 현재 노드 뒤로 list2를 이어준다.
node.next = list2
break

if not list2: # list2가 비어있으면 현재 노드 뒤로 list1을 이어준다.
node.next = list1
break

# 값을 비교해서 작은 값을 next 노드에 추가한다.
if list1.val <= list2.val:
node.next = list1 # 새로운 ListNode를 생성하지 않고 list1를 할당해준다.
list1 = list1.next # list1 하나 소비
else:
node.next = list2 # 새로운 ListNode를 생성하지 않고 list2를 할당해준다.
list2 = list2.next # list2 하나 소비

# 다음 노드를 현재 노드로 할당해준다
node = node.next

# 한쪽 리스트가 소진되면 남은 노드들을 한 번에 연결
current.next = list1 if list1 else list2

# dummy 노드의 다음부터가 실제 정렬된 리스트의 시작
return dummy.next


# 4. 예시 입력으로 호출 및 테스트
# if __name__ == "__main__":
# list1 = make_linked_list([1, 2, 4])
# list2 = make_linked_list([1, 3, 4])

# solution = Solution()
# merged_list = solution.mergeTwoLists(list1, list2)

# print(merged_list)
90 changes: 31 additions & 59 deletions word-search/okyungjin.py
Comment thread
okyungjin marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,89 +1,61 @@
# https://leetcode.com/problems/word-search/

# board: 영문 대/소문자 배열, m * n
# board의 상하좌우로 인접한 문자열을 이어붙여서 word를 완성할 수 있는지 여부를 반환한다.
# 단, 셀은 한번만 사용 가능하다

# dfs를 통해 백트랙킹으로 정답을 찾도록 구현했다.
# [접근법]
# dfs로 문자열을 상,하,좌,우로 탐색한다.

# [복잡도]
# L: word 의 길이
# 시간 복잡도: O(m * n * 4^L)
# 공간 복잡도: O(m * n + L)
class SolutionA:
# 시간 복잡도: O(m * n * 3^L), 이전 문자에서 이동한 방향은 제외하므로 3^L
# 공간 복잡도: O(L)

class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
row_size = len(board)
col_size = len(board[0])

# 방문 여부를 기록하는 2차원 배열
visited = [[False] * col_size for _ in range(row_size)]

def dfs(row, col, str_idx) -> bool:
if str_idx == len(word):
return True

# 종료조건1: 보드의 좌표를 넘어가는 경우
if row < 0 or row >= row_size or col < 0 or col >= col_size:
# board에 word를 만들 수 있는 문자열이 충분히 있는지 확인
for char in set(word):
# board 전체에서 해당 알파벳이 몇 개인지 카운트한다.
board_char_count = sum(row.count(char) for row in board)
if board_char_count < word.count(char):
return False

# 종료조건 2: 이미 방문함
# 종료조건 3: 문자열 불일치
if visited[row][col] or board[row][col] != word[str_idx]:
return False


visited[row][col] = True

# 차례대로 상, 하, 좌, 우의 좌표를 탐색
found = (dfs(row - 1, col, str_idx + 1) or \
dfs(row + 1, col, str_idx + 1) or \
dfs(row, col - 1, str_idx + 1) or \
dfs(row, col + 1, str_idx + 1))

visited[row][col] = False # 백트래킹

return found

# 빈도수가 더 적은 글자부터 탐색하도록 단어를 뒤집는다
count_first = sum(row.count(word[0]) for row in board)
count_last = sum(row.count(word[-1]) for row in board)

if count_first > count_last:
word = word[::-1]
Comment on lines +27 to +32

@parkhojeong parkhojeong Jul 19, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

인풋의 패턴을 파악해서 최적화 한다는 점에선 좋은 접근인 거 같습니다. 다만 첫글자와 마지막 글자의 빈도만 비교해서 최적화하는건 특수한 케이스만 커버가 될 거 같아요. 리트코드 채점 데이터셋엔 이 경우가 들어간 거 같더라구요.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 넵넵 감사합니다 : )


# 시작점을 찾는다
for r in range(row_size):
for c in range(col_size):
if board[r][c] == word[0] and dfs(r, c, 0):
return True

return False

# SolutionA 의 공간복잡도 최적화 버전, visited 제거
# L: word 의 길이
# 시간 복잡도: O(m * n * 4^L)
# 공간 복잡도: O(L)
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
VISITED_MARK = '#'

row_size = len(board)
col_size = len(board[0])

def dfs(row, col, str_idx) -> bool:
def dfs(row: int, col: int, str_idx: int) -> bool:
if str_idx == len(word):
return True

# 종료조건1: 보드의 좌표를 넘어가는 경우
if row < 0 or row >= row_size or col < 0 or col >= col_size:
if not (0 <= row < row_size and 0 <= col < col_size):
return False

# 종료조건 2: 이미 방문함
# 종료조건 2: 이미 방문함 (#: 방문했음을 표시하는 마크)
# 종료조건 3: 문자열 불일치
if board[row][col] == VISITED_MARK or board[row][col] != word[str_idx]:
return False


# VISITED_MARK로 교체하여 방문을 기록
# 방문을 표시하는 마크(#)로 임시 교체
origin_char = board[row][col]
board[row][col] = VISITED_MARK

# 차례대로 상, 하, 좌, 우의 좌표를 탐색
found = (dfs(row - 1, col, str_idx + 1) or \
dfs(row + 1, col, str_idx + 1) or \
dfs(row, col - 1, str_idx + 1) or \
dfs(row, col + 1, str_idx + 1))
# 좌표를 탐색한다
found = (
dfs(row - 1, col, str_idx + 1) # 상
or dfs(row + 1, col, str_idx + 1) # 하
or dfs(row, col - 1, str_idx + 1) # 좌
or dfs(row, col + 1, str_idx + 1) # 우
)

# 원본 문자열로 복원
board[row][col] = origin_char
Expand Down
Loading