-
-
Notifications
You must be signed in to change notification settings - Fork 335
[liza0525] WEEK 10 Solutions #2593
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9d0d57a
1edf5d6
6e21b3b
3114a38
ac68423
5650163
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
풀이 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 |
|---|---|---|
|
|
@@ -63,3 +63,35 @@ def findMinBinarySearch(self, nums: List[int]) -> int: | |
| # # 파이썬의 내장함수인 min을 이용해도 문제 풀이 가능 | ||
| # # https://wiki.python.org/moin/TimeComplexity애 의하면 min 함수도 O(n)임 | ||
| # return min(nums) | ||
|
|
||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 트리의 모든 노드를 한 번씩 방문하며, 재귀 호출로 인해 최대 재귀 깊이만큼 공간이 필요하다. 개선 제안: 현재 구현이 적절해 보입니다. |
| 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 |
| 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 |
| 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 그래프의 노드 수와 간선 수에 비례하는 DFS 기반 사이클 검사를 수행하며, 각 노드와 간선에 대해 한 번씩 탐색한다.
개선 제안: 현재 구현이 적절해 보입니다.