-
-
Notifications
You must be signed in to change notification settings - Fork 362
[alphaorderly] WEEK 04 Solutions #2737
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
base: main
Are you sure you want to change the base?
Changes from all commits
4d5f428
3ac63f5
2536209
612dbbb
a7c2098
79f6f86
3855235
33ab6bc
b1de0e1
f9bf16c
de3390f
b3dc7d7
17669b6
d8b5903
a14205b
b087a32
2380e81
e51ae37
9592367
555e26d
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,72 @@ | ||
| """ | ||
| Time Complexity: O(n * amount) | ||
| Space Complexity: O(n * amount) | ||
|
|
||
| - DP / Top-Down approach | ||
| - Use a cache to store the results of the subproblems | ||
| """ | ||
| class Solution: | ||
| def coinChange(self, coins: List[int], amount: int) -> int: | ||
| LARGE = 10 ** 5 | ||
|
|
||
| @cache | ||
| def dp(coin: int, left: int) -> int: | ||
| if left == 0: | ||
| return 0 | ||
|
|
||
| if left < 0 or coin >= len(coins): | ||
| return LARGE | ||
|
|
||
| a = dp(coin, left - coins[coin]) + 1 | ||
| b = dp(coin + 1, left) | ||
|
|
||
| return min(a, b) | ||
|
|
||
| ans = dp(0, amount) | ||
|
|
||
| return ans if ans != LARGE else -1 | ||
|
|
||
| """ | ||
| Time Complexity: O(n * amount) | ||
| Space Complexity: O(n * amount) | ||
|
|
||
| - DP / Bottom-Up approach | ||
| - Use a 2D array to store the results of the subproblems | ||
| """ | ||
| class Solution: | ||
| def coinChange(self, coins: List[int], amount: int) -> int: | ||
| N = len(coins) | ||
| dp = [[float('inf')] * (amount + 1) for _ in range(N)] | ||
|
|
||
| for c in range(N): | ||
| dp[c][0] = 0 | ||
|
|
||
| if c > 0: | ||
| for a in range(amount + 1): | ||
| dp[c][a] = dp[c - 1][a] | ||
|
|
||
| for a in range(coins[c], amount + 1): | ||
| dp[c][a] = min(dp[c][a], dp[c][a - coins[c]] + 1) | ||
|
|
||
| ans = dp[-1][-1] | ||
|
|
||
| return ans if ans != float('inf') else -1 | ||
|
|
||
| """ | ||
| Time Complexity: O(n * amount) | ||
| Space Complexity: O(amount) | ||
|
|
||
| - DP / Bottom-Up approach [Space Optimized] | ||
| - Use a 1D array to store the results of the subproblems | ||
| """ | ||
| class Solution: | ||
| def coinChange(self, coins: List[int], amount: int) -> int: | ||
| N = len(coins) | ||
| dp = [float("inf")] * (amount + 1) | ||
| dp[0] = 0 | ||
|
|
||
| for c in range(N): | ||
| for a in range(coins[c], amount + 1): | ||
| dp[a] = min(dp[a], dp[a - coins[c]] + 1) | ||
|
|
||
| return dp[-1] if dp[-1] != float("inf") else -1 |
|
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(log N) |
| Space | O(1) |
피드백: 양쪽 끝과 중간 값을 비교해 최소값의 위치를 이진 탐색으로 좁힙니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 2: Solution.findMin — Time: O(N) / Space: O(1)
| 복잡도 | |
|---|---|
| Time | O(N) |
| Space | O(1) |
피드백: 최적의 풀이가 아니므로 테스트용 대안으로 남겨둔 형태입니다.
개선 제안: 현재 구현이 적절해 보입니다.
💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| """ | ||
| Time Complexity: O(log N) | ||
| Space Complexity: O(1) | ||
|
|
||
| ### Binary Search Solution ### | ||
|
|
||
| 1. Initialize two pointers, left and right, to the start and end of the array | ||
| 2. While left is less than right, calculate the middle index | ||
| 3. If nums[mid] > nums[right], search the right half (left = mid + 1) | ||
| - Why? mid is in the left sorted portion (larger values), so the rotation point (minimum) must be to the right of mid | ||
| 4. Otherwise, search the left half including mid (right = mid) | ||
| - nums[mid] <= nums[right] means mid is in the right sorted portion or the array is not rotated, so the minimum is at mid or to its left | ||
| 5. When left == right, return nums[right] | ||
| """ | ||
| class Solution: | ||
| def findMin(self, nums: List[int]) -> int: | ||
| N = len(nums) | ||
| left, right = 0, N - 1 | ||
|
|
||
| while left < right: | ||
| mid = (left + right) // 2 | ||
|
|
||
| if nums[mid] > nums[right]: | ||
| left = mid + 1 | ||
| else: | ||
| right = mid | ||
|
|
||
| return nums[right] | ||
|
|
||
| """ | ||
| Time Complexity: O(N) | ||
| Space Complexity: O(1) | ||
|
|
||
| ### Linear Search Solution ### ( NOT RECOMMENDED ) | ||
|
|
||
| 1. Return the minimum element in the array | ||
| """ | ||
| class Solution: | ||
| def findMin(self, nums: List[int]) -> int: | ||
| return min(nums) |
|
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(N) |
피드백: 노드 수에 비례하는 재귀 스택을 사용합니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 2: Solution.maxDepth — Time: O(N) / Space: O(N)
| 복잡도 | |
|---|---|
| Time | O(N) |
| Space | O(N) |
피드백: 명시적 스택으로 깊이를 추적합니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 3: Solution.maxDepth — Time: O(N) / Space: O(N)
| 복잡도 | |
|---|---|
| Time | O(N) |
| Space | O(N) |
피드백: 큐를 사용해 레벨별로 깊이를 업데이트합니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 4: Solution.maxDepth — Time: O(N) / Space: O(N)
| 복잡도 | |
|---|---|
| Time | O(N) |
| Space | O(N) |
피드백: 최종 깊이를 레벨 변수로 추적하는 BFS 변형입니다.
개선 제안: 현재 구현이 적절해 보입니다.
💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| # 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 | ||
| """ | ||
| Time Complexity: O(N) | ||
| Space Complexity: O(N) - Recursive stack space | ||
|
|
||
| ### Recursive Solution ( DFS ) ### | ||
|
|
||
| 1. If the root is None, return 0 | ||
| 2. Return the maximum of the depth of the left and right subtrees + 1 | ||
| """ | ||
| class Solution: | ||
| def maxDepth(self, root: Optional[TreeNode]) -> int: | ||
| if not root: | ||
| return 0 | ||
|
|
||
| return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1 | ||
|
|
||
| """ | ||
| Time Complexity: O(N) | ||
| Space Complexity: O(N) - Stack space | ||
|
|
||
| ### Iterative Solution ( DFS ) ### | ||
|
|
||
| 1. If the root is None, return 0 | ||
| 2. Use a stack to store the nodes and the level of the nodes | ||
| 3. Pop the nodes from the stack and update the maximum depth | ||
| 4. If the node has a left child, add the left child and the level + 1 to the stack | ||
| 5. If the node has a right child, add the right child and the level + 1 to the stack | ||
| 6. Return the maximum depth | ||
| """ | ||
| class Solution: | ||
| def maxDepth(self, root: Optional[TreeNode]) -> int: | ||
| if not root: | ||
| return 0 | ||
|
|
||
| s = [(root, 1)] | ||
| ans = 1 | ||
|
|
||
| while s: | ||
| node, level = s.pop() | ||
| ans = max(ans, level) | ||
|
|
||
| if node.left: | ||
| s.append((node.left, level + 1)) | ||
|
|
||
| if node.right: | ||
| s.append((node.right, level + 1)) | ||
|
|
||
| return ans | ||
|
|
||
| """ | ||
| Time Complexity: O(N) | ||
| Space Complexity: O(N) - Queue space | ||
|
|
||
| ### Iterative Solution ( BFS ) ### | ||
|
|
||
| 1. If the root is None, return 0 | ||
| 2. Use a queue to store the nodes and the level of the nodes | ||
| 3. Pop the nodes from the queue and update the maximum depth | ||
| 4. If the node has a left child, add the left child and the level + 1 to the queue | ||
| 5. If the node has a right child, add the right child and the level + 1 to the queue | ||
| 6. Return the maximum depth | ||
| """ | ||
| class Solution: | ||
| def maxDepth(self, root: Optional[TreeNode]) -> int: | ||
| if not root: | ||
| return 0 | ||
|
|
||
| q = deque([(root, 1)]) | ||
| ans = 1 | ||
|
|
||
| while q: | ||
| node, level = q.popleft() | ||
| ans = max(ans, level) | ||
|
|
||
| if node.left: | ||
| q.append((node.left, level + 1)) | ||
|
|
||
| if node.right: | ||
| q.append((node.right, level + 1)) | ||
|
|
||
| return ans | ||
|
|
||
| """ | ||
| Time Complexity: O(N) | ||
| Space Complexity: O(N) - Queue space | ||
|
|
||
| ### Iterative Solution ( Level Order Traversal ) ### | ||
|
|
||
| 1. If the root is None, return 0 | ||
| 2. Use a queue to store the nodes | ||
| 3. Pop the nodes from the queue and update the maximum depth | ||
| 4. If the node has a left child, add the left child to the queue | ||
| 5. If the node has a right child, add the right child to the queue | ||
| 6. Return the maximum depth | ||
| """ | ||
| class Solution: | ||
| def maxDepth(self, root: Optional[TreeNode]) -> int: | ||
| if not root: | ||
| return 0 | ||
|
|
||
| q = deque([root]) | ||
| level = 0 | ||
|
|
||
| while q: | ||
| level += 1 | ||
| N = len(q) | ||
| for _ in range(N): | ||
| node = q.popleft() | ||
|
|
||
| if node.left: | ||
| q.append(node.left) | ||
|
|
||
| if node.right: | ||
| q.append(node.right) | ||
|
|
||
| return level |
|
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.mergeTwoLists — Time: O(N) / Space: O(N)
| 복잡도 | |
|---|---|
| Time | O(N) |
| Space | O(N) |
피드백: 재귀 깊이가 증가할 경우 스택 사용이 늘어납니다.
개선 제안: 현재 구현이 적절해 보입니다.
💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| # Definition for singly-linked list. | ||
| # class ListNode: | ||
| # def __init__(self, val=0, next=None): | ||
| # self.val = val | ||
| # self.next = next | ||
| """ | ||
| Time Complexity: O(N) | ||
| Space Complexity: O(1) | ||
|
|
||
| 1. Create a dummy node to store the head of the merged list ( return_node ) | ||
| 2. Traverse the two lists until one of the lists is empty | ||
| 3. Compare the values of the two nodes and add the smaller node to the dummy node | ||
| 4. Move the pointer of the list with the smaller node to the next node | ||
| 5. Add the remaining nodes of the non-empty list to the dummy node | ||
| 6. Return the next node of the dummy node | ||
| """ | ||
| class Solution: | ||
| def mergeTwoLists( | ||
| self, list1: Optional[ListNode], list2: Optional[ListNode] | ||
| ) -> Optional[ListNode]: | ||
| head = ListNode() | ||
| node = head | ||
|
|
||
| while list1 and list2: | ||
| if list1.val > list2.val: | ||
| head.next = list2 | ||
| list2 = list2.next | ||
| else: | ||
| head.next = list1 | ||
| list1 = list1.next | ||
|
|
||
| head = head.next | ||
|
|
||
| head.next = list1 or list2 | ||
|
|
||
| return node.next | ||
|
|
||
| """ | ||
| Time Complexity: O(N) | ||
| Space Complexity: O(N) - Recursive stack space | ||
|
|
||
| 1. If one of the lists is empty, return the other list | ||
| 2. Compare the values of the two nodes and add the smaller node to the merged list | ||
| 3. Move the pointer of the list with the smaller node to the next node | ||
| 4. Return the next node of the merged list | ||
| """ | ||
| class Solution: | ||
| def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: | ||
| if not list1 or not list2: | ||
| return list1 or list2 | ||
|
|
||
| if list1.val > list2.val: | ||
| list2.next = self.mergeTwoLists(list1, list2.next) | ||
| return list2 | ||
| else: | ||
| list1.next = self.mergeTwoLists(list1.next, list2) | ||
| return list1 |
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.
🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
풀이 1:
Solution.coinChange— Time: O(n * amount) / Space: O(n * amount)피드백: 하나의 금액에 대해 각 코인 위치별로 서브문제를 cache로 재사용하여 중복을 제거합니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 2:
Solution.coinChange— Time: O(n * amount) / Space: O(n * amount)피드백: 코인 인덱스와 남은 금액의 조합을 상태로 하여 모든 경우를 탐색합니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 3:
Solution.coinChange— Time: O(n * amount) / Space: O(amount)피드백: 거기에 코인 순회를 바꿔가며 가능한 최소 개수를 갱신합니다. 추가 공간 절약이 가능해졌습니다.
개선 제안: 현재 구현이 적절해 보입니다.