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
48 changes: 48 additions & 0 deletions course-schedule/liza0525.py
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.

🏷️ 알고리즘 패턴 분석

  • 패턴: DFS
  • 설명: 이 코드는 그래프의 사이클 검사를 위해 깊이 우선 탐색(DFS)를 사용하며, 과목 간 의존 관계를 재귀적으로 탐색하는 방식입니다.

📊 시간/공간 복잡도 분석

복잡도
Time O(V + E)
Space O(V + E)

피드백: 그래프의 노드 수와 간선 수에 비례하는 DFS 기반 사이클 검사를 수행하며, 각 노드와 간선에 대해 한 번씩 탐색한다.

개선 제안: 현재 구현이 적절해 보입니다.

💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from collections import defaultdict

# 7기 풀이
# 시간 복잡도: O(V + E)
# - numCourses(V)와 prerequisites의 길이(E)에 비례
# 공간 복잡도: O(V)
# - 각 과목의 상태를 계산하는 state와 prereq_map의 공간은 numCourses(V)에 비례

BEFORE_START = 0
IN_PROGRESS = 1
DONE = 2
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
# states: 각 과목의 상태 저장, index가 곧 과목을 나타냄
states = [BEFORE_START] * numCourses

# 선수과목(key) 당 다음 과목들(value) 정보 저장
prereq_map = defaultdict(list)

for next_course, pre_course in prerequisites:
prereq_map[pre_course].append(next_course)

def dfs(course):
if states[course] != BEFORE_START:
# 이미 들었거나, 듣는 중이라면 더이상 탐색하지 않아도 됨
return

states[course] = IN_PROGRESS # 현재 과목을 듣는 중 상태로 변경

next_courses = prereq_map.get(course, []) # 다음 과목의 정보 가져오기

for next_course in next_courses:
# 다음 과목들도 모두 돌아본다
if states[next_course] == IN_PROGRESS:
# 다음 과목을 듣고 있는 중이라면, 서로가 선수과목이 된다는 이야기(그래프의 사이클이 생김)
# 이 때는 더이상 탐색하지 않고 return한다.
return
# 다음 과목에 대한 탐색
dfs(next_course)

# 다음 과목들도 다 들었다고 하면 사이클이 없으므로 해당 과목도 들었음 상태로 변경
states[course] = DONE


for course in range(numCourses):
dfs(course)

return IN_PROGRESS not in states # 듣는 중 상태의 존재 여부를 return
32 changes: 32 additions & 0 deletions find-minimum-in-rotated-sorted-array/liza0525.py
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.

🏷️ 알고리즘 패턴 분석

  • 패턴: Binary Search
  • 설명: 이 코드는 회전된 정렬 배열에서 최소값을 찾기 위해 이진 탐색을 활용하여 탐색 범위를 절반씩 줄이는 방식을 사용합니다.

📊 시간/공간 복잡도 분석

ℹ️ 이 파일에는 3가지 풀이가 포함되어 있어 각각 분석합니다.

풀이 1: Solution.findMin — Time: O(n) / Space: O(1)
복잡도
Time O(n)
Space O(1)

피드백: 배열 전체를 한 번 순회하며 회전 지점을 찾기 때문에 시간 복잡도는 선형이며, 별도의 추가 공간이 필요 없다.

개선 제안: 현재 구현이 적절해 보입니다.

풀이 2: Solution.findMinBinarySearch — Time: O(log n) / Space: O(1)
복잡도
Time O(log n)
Space O(1)

피드백: 이진 탐색을 통해 배열의 회전 지점을 효율적으로 찾으며, 변수 몇 개만 사용한다.

개선 제안: 현재 구현이 적절해 보입니다.

풀이 3: Solution.findMin (추가 주석 버전) — Time: O(n) / Space: O(1)
복잡도
Time O(n)
Space O(1)

피드백: 단순히 배열 전체를 순회하여 최소값을 찾기 때문에 시간 복잡도는 선형이다.

개선 제안: 현재 구현이 적절해 보입니다.

💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!

Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,35 @@ def findMinBinarySearch(self, nums: List[int]) -> int:
# # 파이썬의 내장함수인 min을 이용해도 문제 풀이 가능
# # https://wiki.python.org/moin/TimeComplexity애 의하면 min 함수도 O(n)임
# return min(nums)

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.

해당 문제는 4주차의 문제이지만 기존에 다른 폴더 내에 답을 잘못 저장해둬서
올바르게 옮겨둡니다.


# 7기 풀이
# 시간 복잡도: O(log n)
# - binary search를 이용했기 때문에 최악의 경우는 log n
# 공간 복잡도: O(1)
# - 몇 가지 변수만 사용됨
class Solution:
def findMin(self, nums: List[int]) -> int:
# 양 끝을 먼저 잡아준다
left, right = 0, len(nums) - 1

# left보다 right가 클 때 계속 루프를 돈다
# 이는 left와 right가 같아질 때까지 돈다는 말과 동일하다
while left < right:
# 중간 index 계산
mid = (left + right) // 2

if nums[mid] > nums[right]:
# 중간 index의 값이 오른쪽 index의 값보다 크다는 것은
# 최소 값이 mid 기준 오른쪽 구역에 존재하기 때문에 left을 mid쪽으로 움직인다.
# mid의 값 자체는 최소값이 될 수 없기 때문에 left는 이보다 한 칸 옆으로 움직여야 함
left = mid + 1
else:
# 중간 index의 값이 오른쪽 index의 값보다 작다는 것은
# 최소 값이 mid 기준 왼쪽 구역에 존재하기 때문에 right를 mid쪽으로 움직인다.
# mid의 값 자체는 최소값이 될 수 있기 때문에 right는 mid 값을 할당한다.
right = mid

# left와 right가 같아질 때(최소값을 찾았을 때) 루프 탈출을 하기 때문에
# nums[left] 또는 nums[right]를 반환
return nums[left]
20 changes: 20 additions & 0 deletions invert-binary-tree/liza0525.py
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.

🏷️ 알고리즘 패턴 분석

  • 패턴: Binary Search, Recursion
  • 설명: 이 코드는 재귀를 이용하여 이진 트리의 노드들을 순회하며 좌우 자식을 교환하는 방식으로 작동합니다. 트리 구조를 탐색하는 재귀적 접근이 핵심입니다.

📊 시간/공간 복잡도 분석

유저 분석 실제 분석 결과
Time O(n) O(n)
Space O(h) O(h)

피드백: 트리의 모든 노드를 한 번씩 방문하며, 재귀 호출로 인해 최대 재귀 깊이만큼 공간이 필요하다.

개선 제안: 현재 구현이 적절해 보입니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 7기 풀이
# 시간 복잡도: O(n)
# - 노드의 개수(n)만큼 시간 소요
# 공간 복잡도: O(h)
# - 주어진 트리의 높이(h)만큼 재귀 스택 사용
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
def invert_binary_tree(node):
if not node:
return

# 트리 탐색 시 right -> left 순서로 순회하도록 재귀 호출
invert_binary_tree(node.right)
invert_binary_tree(node.left)

# node의 right와 left를 서로 바꿔준다.
node.right, node.left = node.left, node.right

invert_binary_tree(root)
return root
22 changes: 22 additions & 0 deletions jump-game/liza0525.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# 7기 풀이
# 시간 복잡도: O(n)
# - nums의 길이(n) 만큼 시간 소요
# 공간 복잡도: O(1)
# - 변수(max_reach)만 사용
class Solution:
def canJump(self, nums: List[int]) -> bool:
max_reach = 0 # 매 순간에 가장 멀리 갈 수 있는 index 저장

for i in range(len(nums)):
if i > max_reach:
# 현재의 인덱스가 max_reach보다 크면, 도달 자체를 할 수 없음을 의미
# 따라서 False로 early return
return False

# 기존의 max_reach와 i에서 최대로 갈 수 있는 거리를 계산한 값을 비교하여
# 더 큰값으로 max_reach 업데이트
max_reach = max(max_reach, i + nums[i])

# for문을 모두 돌았다는 것은 맨 끝 인덱스에 도달 가능하다는 것을 의미
# True로 리턴한다.
return True
32 changes: 32 additions & 0 deletions merge-k-sorted-lists/liza0525.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import heapq

# 7기 풀이
# 시간 복잡도: O(n log k)
# - while문의 매 루프마다 최소힙 계산(리스트의 개수 k에 입각) * 전체 노드 수(n) 만큼 최대 시간복잡도 나옴
# 공간 복잡도: O(k)
# - 리스트의 개수(k) 만큼의 heap 사이즈가 나옴
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
dummy = ListNode()
head = dummy # 초기 head를 dummy로 지정

heap = [] # 최소힙 계산을 위한 heap

# 먼저 힙에 노드를 push
for i, node in enumerate(lists):
if node:
# heap push 기준은 node.val과 node가 위치한 index를 기준으로
heapq.heappush(heap, (node.val, i, node))

# heap의 모든 노드를 pop할 때까지 while 루프 돌린다
while heap:
_, i, node = heapq.heappop(heap) # 최소값이 들어있는 node를 pop
head.next = node # head의 next에 현재의 node를 저장 후
head = head.next # head 다음 노드로 변경

if node.next:
# node에 next 노드가 있었다면 해당 노드의 val을 기준으로 heap에 push한다
heapq.heappush(heap, (node.next.val, i, node.next))

# dummy 노드였으므로 next node를 return
return dummy.next
44 changes: 44 additions & 0 deletions search-in-rotated-sorted-array/liza0525.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# NOTE: 문제 위치를 잘못 잡음 4주차 find-minimum-in-rotated-sorted-array로 옮겨감
# 삭제는 추후에(이유: github 리뷰 시 diff가 생겨 리뷰가 불편해질 수 있음)
# 7기 풀이
# 시간 복잡도: O(log n)
# - binary search를 이용했기 때문에 최악의 경우는 log n
Expand Down Expand Up @@ -28,3 +30,45 @@ def findMin(self, nums: List[int]) -> int:
# left와 right가 같아질 때(최소값을 찾았을 때) 루프 탈출을 하기 때문에
# nums[left] 또는 nums[right]를 반환
return nums[left]


# 7기 풀이
# 시간 복잡도: O(log n)
# - binary search를 하므로 nums의 길이(n)의 로그 값 만큼의 시간 소요
# 공간 복잡도: O(1)
# - 변수 몇 개만 사용
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1 # 양 끝을 left, right로 설정

while left <= right:
mid = (left + right) // 2 # 중간 값 설정

if target == nums[mid]:
# 중간 값이 곧 target값이면 중간 index를 return
return mid
elif nums[left] <= nums[mid]:
# left ~ mid 사이가 정렬이 되어 있다면,
# (비고: 문제 조건 상, sorted list에서 rotate한 상황이므로
# left와 mid만 비교해도 내부는 sorted 되어 있음
# 이는 right ~ mid 비교 때와 동일함)
if nums[left] <= target < nums[mid]:
# target이 오름차순이 되어 있는 구간에 있는지 확인
# 맞다면 right를 변경
right = mid - 1
else:
# 오름차순 쪽이 아닌 곳에 있다면 rotate되어 순서가 뒤틀린 쪽에 있다는 의미라
# left를 변경
left = mid + 1
elif nums[mid] <= nums[right]:
# mid ~ right 사이가 정렬이 되어 있다면,
# 마찬가지로 오름차순 구간에 target이 존재하는지 아닌지를 판별하여
# left와 right를 조절한다
if nums[mid] < target <= nums[right]:
left = mid + 1
else:
right = mid - 1

# while문을 모두 돌았음에도 return이 안되었다는 것은
# target을 찾지 못했다는 의미이므로 문제 조건에 따라 -1 리턴
return -1
Loading