Skip to content

Commit a5bf00e

Browse files
authored
[alphaorderly] WEEK 04 Solutions (#2737)
* week 1 * [alphaorderly] WEEK 02 Solutions * fix: description์— ๋งž๊ฒŒ ์ฝ”๋“œ ์ˆ˜์ • * fix: ์ข€ ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ˆ˜์ • * [alphaorderly] WEEK 03 Solutions * fix: ๋ถˆํ•„์š”ํ•œ ์ฝ”๋“œ ์‚ญ์ œ * fix: ์ค„๋ฐ”๊ฟˆ ๋ฆฐํŠธ ๋ฌธ์ œ ์ˆ˜์ • * [alphaorderly] WEEK 03 Solutions * fix: ๋ฆฐํŠธ ์˜ค๋ฅ˜ ์ˆ˜์ • * fix: ํŒŒ์ด์ฌ ๋‚ด์žฅํ•จ์ˆ˜ ์‚ฌ์šฉ ์ฝ”๋“œ ์ถ”๊ฐ€ * fix: valid-palindrome ๋ฌธ์ œ์— ํˆฌํฌ์ธํ„ฐ ๊ตฌํ˜„ ๋‹ต์•ˆ ์ถ”๊ฐ€ * fix: ๋กœ์ง ๊ฐ€๋…์„ฑ ์ˆ˜์ • * [alphaorderly] WEEK 04 Solutions - draft * fix: ์ฝ”๋“œ ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ
1 parent 9f896bb commit a5bf00e

5 files changed

Lines changed: 438 additions & 0 deletions

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""
2+
Time Complexity: O(n * amount)
3+
Space Complexity: O(n * amount)
4+
5+
- DP / Top-Down approach
6+
- Use a cache to store the results of the subproblems
7+
"""
8+
class Solution:
9+
def coinChange(self, coins: List[int], amount: int) -> int:
10+
LARGE = 10 ** 5
11+
12+
@cache
13+
def dp(coin: int, left: int) -> int:
14+
if left == 0:
15+
return 0
16+
17+
if left < 0 or coin >= len(coins):
18+
return LARGE
19+
20+
a = dp(coin, left - coins[coin]) + 1
21+
b = dp(coin + 1, left)
22+
23+
return min(a, b)
24+
25+
ans = dp(0, amount)
26+
27+
return ans if ans != LARGE else -1
28+
29+
"""
30+
Time Complexity: O(n * amount)
31+
Space Complexity: O(n * amount)
32+
33+
- DP / Bottom-Up approach
34+
- Use a 2D array to store the results of the subproblems
35+
"""
36+
class Solution:
37+
def coinChange(self, coins: List[int], amount: int) -> int:
38+
N = len(coins)
39+
dp = [[float('inf')] * (amount + 1) for _ in range(N)]
40+
41+
for c in range(N):
42+
dp[c][0] = 0
43+
44+
if c > 0:
45+
for a in range(amount + 1):
46+
dp[c][a] = dp[c - 1][a]
47+
48+
for a in range(coins[c], amount + 1):
49+
dp[c][a] = min(dp[c][a], dp[c][a - coins[c]] + 1)
50+
51+
ans = dp[-1][-1]
52+
53+
return ans if ans != float('inf') else -1
54+
55+
"""
56+
Time Complexity: O(n * amount)
57+
Space Complexity: O(amount)
58+
59+
- DP / Bottom-Up approach [Space Optimized]
60+
- Use a 1D array to store the results of the subproblems
61+
"""
62+
class Solution:
63+
def coinChange(self, coins: List[int], amount: int) -> int:
64+
N = len(coins)
65+
dp = [float("inf")] * (amount + 1)
66+
dp[0] = 0
67+
68+
for c in range(N):
69+
for a in range(coins[c], amount + 1):
70+
dp[a] = min(dp[a], dp[a - coins[c]] + 1)
71+
72+
return dp[-1] if dp[-1] != float("inf") else -1
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""
2+
Time Complexity: O(log N)
3+
Space Complexity: O(1)
4+
5+
### Binary Search Solution ###
6+
7+
1. Initialize two pointers, left and right, to the start and end of the array
8+
2. While left is less than right, calculate the middle index
9+
3. If nums[mid] > nums[right], search the right half (left = mid + 1)
10+
- Why? mid is in the left sorted portion (larger values), so the rotation point (minimum) must be to the right of mid
11+
4. Otherwise, search the left half including mid (right = mid)
12+
- 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
13+
5. When left == right, return nums[right]
14+
"""
15+
class Solution:
16+
def findMin(self, nums: List[int]) -> int:
17+
N = len(nums)
18+
left, right = 0, N - 1
19+
20+
while left < right:
21+
mid = (left + right) // 2
22+
23+
if nums[mid] > nums[right]:
24+
left = mid + 1
25+
else:
26+
right = mid
27+
28+
return nums[right]
29+
30+
"""
31+
Time Complexity: O(N)
32+
Space Complexity: O(1)
33+
34+
### Linear Search Solution ### ( NOT RECOMMENDED )
35+
36+
1. Return the minimum element in the array
37+
"""
38+
class Solution:
39+
def findMin(self, nums: List[int]) -> int:
40+
return min(nums)
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Definition for a binary tree node.
2+
# class TreeNode:
3+
# def __init__(self, val=0, left=None, right=None):
4+
# self.val = val
5+
# self.left = left
6+
# self.right = right
7+
"""
8+
Time Complexity: O(N)
9+
Space Complexity: O(N) - Recursive stack space
10+
11+
### Recursive Solution ( DFS ) ###
12+
13+
1. If the root is None, return 0
14+
2. Return the maximum of the depth of the left and right subtrees + 1
15+
"""
16+
class Solution:
17+
def maxDepth(self, root: Optional[TreeNode]) -> int:
18+
if not root:
19+
return 0
20+
21+
return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1
22+
23+
"""
24+
Time Complexity: O(N)
25+
Space Complexity: O(N) - Stack space
26+
27+
### Iterative Solution ( DFS ) ###
28+
29+
1. If the root is None, return 0
30+
2. Use a stack to store the nodes and the level of the nodes
31+
3. Pop the nodes from the stack and update the maximum depth
32+
4. If the node has a left child, add the left child and the level + 1 to the stack
33+
5. If the node has a right child, add the right child and the level + 1 to the stack
34+
6. Return the maximum depth
35+
"""
36+
class Solution:
37+
def maxDepth(self, root: Optional[TreeNode]) -> int:
38+
if not root:
39+
return 0
40+
41+
s = [(root, 1)]
42+
ans = 1
43+
44+
while s:
45+
node, level = s.pop()
46+
ans = max(ans, level)
47+
48+
if node.left:
49+
s.append((node.left, level + 1))
50+
51+
if node.right:
52+
s.append((node.right, level + 1))
53+
54+
return ans
55+
56+
"""
57+
Time Complexity: O(N)
58+
Space Complexity: O(N) - Queue space
59+
60+
### Iterative Solution ( BFS ) ###
61+
62+
1. If the root is None, return 0
63+
2. Use a queue to store the nodes and the level of the nodes
64+
3. Pop the nodes from the queue and update the maximum depth
65+
4. If the node has a left child, add the left child and the level + 1 to the queue
66+
5. If the node has a right child, add the right child and the level + 1 to the queue
67+
6. Return the maximum depth
68+
"""
69+
class Solution:
70+
def maxDepth(self, root: Optional[TreeNode]) -> int:
71+
if not root:
72+
return 0
73+
74+
q = deque([(root, 1)])
75+
ans = 1
76+
77+
while q:
78+
node, level = q.popleft()
79+
ans = max(ans, level)
80+
81+
if node.left:
82+
q.append((node.left, level + 1))
83+
84+
if node.right:
85+
q.append((node.right, level + 1))
86+
87+
return ans
88+
89+
"""
90+
Time Complexity: O(N)
91+
Space Complexity: O(N) - Queue space
92+
93+
### Iterative Solution ( Level Order Traversal ) ###
94+
95+
1. If the root is None, return 0
96+
2. Use a queue to store the nodes
97+
3. Pop the nodes from the queue and update the maximum depth
98+
4. If the node has a left child, add the left child to the queue
99+
5. If the node has a right child, add the right child to the queue
100+
6. Return the maximum depth
101+
"""
102+
class Solution:
103+
def maxDepth(self, root: Optional[TreeNode]) -> int:
104+
if not root:
105+
return 0
106+
107+
q = deque([root])
108+
level = 0
109+
110+
while q:
111+
level += 1
112+
N = len(q)
113+
for _ in range(N):
114+
node = q.popleft()
115+
116+
if node.left:
117+
q.append(node.left)
118+
119+
if node.right:
120+
q.append(node.right)
121+
122+
return level
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Definition for singly-linked list.
2+
# class ListNode:
3+
# def __init__(self, val=0, next=None):
4+
# self.val = val
5+
# self.next = next
6+
"""
7+
Time Complexity: O(N)
8+
Space Complexity: O(1)
9+
10+
1. Create a dummy node to store the head of the merged list ( return_node )
11+
2. Traverse the two lists until one of the lists is empty
12+
3. Compare the values of the two nodes and add the smaller node to the dummy node
13+
4. Move the pointer of the list with the smaller node to the next node
14+
5. Add the remaining nodes of the non-empty list to the dummy node
15+
6. Return the next node of the dummy node
16+
"""
17+
class Solution:
18+
def mergeTwoLists(
19+
self, list1: Optional[ListNode], list2: Optional[ListNode]
20+
) -> Optional[ListNode]:
21+
head = ListNode()
22+
node = head
23+
24+
while list1 and list2:
25+
if list1.val > list2.val:
26+
head.next = list2
27+
list2 = list2.next
28+
else:
29+
head.next = list1
30+
list1 = list1.next
31+
32+
head = head.next
33+
34+
head.next = list1 or list2
35+
36+
return node.next
37+
38+
"""
39+
Time Complexity: O(N)
40+
Space Complexity: O(N) - Recursive stack space
41+
42+
1. If one of the lists is empty, return the other list
43+
2. Compare the values of the two nodes and add the smaller node to the merged list
44+
3. Move the pointer of the list with the smaller node to the next node
45+
4. Return the next node of the merged list
46+
"""
47+
class Solution:
48+
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
49+
if not list1 or not list2:
50+
return list1 or list2
51+
52+
if list1.val > list2.val:
53+
list2.next = self.mergeTwoLists(list1, list2.next)
54+
return list2
55+
else:
56+
list1.next = self.mergeTwoLists(list1.next, list2)
57+
return list1

0 commit comments

Comments
ย (0)