Skip to content

Commit 0ef1547

Browse files
authored
Merge pull request #525 from lymchgmk/feat/week9
[EGON]] Week9 Solutions
2 parents 0b457eb + b15bc49 commit 0ef1547

File tree

6 files changed

+372
-38
lines changed

6 files changed

+372
-38
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from typing import List
2+
from unittest import TestCase, main
3+
4+
5+
class Solution:
6+
def findMin(self, nums: List[int]) -> int:
7+
return self.solve_binary_search(nums)
8+
9+
"""
10+
Runtime: 32 ms (Beats 97.56%)
11+
Time Complexity: O(log n)
12+
- 크기가 n인 배열에 대한 이분탐색에 O(log n)
13+
- while 조건문 판단에 O(2), and 연산이므로 단축 평가에 의해 upper bound
14+
- 엣지 케이스 처리를 위한 마지막 lo, hi 2개 항에 대한 min연산에 O(2)
15+
> O(log n) * O(2) + O(2) ~= O(log n)
16+
17+
Memory: 16.82 (Beats 50.00%)
18+
Space Complexity: O(1)
19+
> 이분탐색에 필요한 정수형 변수 lo, hi, mid 3개만 사용했으므로 n과 상관없이 O(1)
20+
"""
21+
def solve_binary_search(self, nums: List[int]) -> int:
22+
lo, hi = 0, len(nums) - 1
23+
while lo < hi and nums[hi] < nums[lo]:
24+
mid = (lo + hi) // 2
25+
if nums[lo] < nums[mid]:
26+
lo = mid
27+
elif nums[mid] < nums[hi]:
28+
hi = mid
29+
else:
30+
break
31+
32+
return min(nums[lo], nums[hi])
33+
34+
35+
class _LeetCodeTestCases(TestCase):
36+
def test_1(self):
37+
nums = [2, 1]
38+
output = 1
39+
self.assertEqual(Solution().findMin(nums), output)
40+
41+
42+
if __name__ == '__main__':
43+
main()

linked-list-cycle/EGON.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from typing import Optional
2+
from unittest import TestCase, main
3+
4+
5+
# Definition for singly-linked list.
6+
class ListNode:
7+
def __init__(self, x):
8+
self.val = x
9+
self.next = None
10+
11+
12+
class Solution:
13+
def hasCycle(self, head: Optional[ListNode]) -> bool:
14+
return self.solve(head)
15+
16+
"""
17+
Runtime: 37 ms (Beats 93.02%)
18+
Time Complexity: O(n)
19+
> head부터 next가 있는 동안 선형적으로 조회하므로 O(n)
20+
21+
Memory: 18.62 (Beats 98.22%)
22+
Space Complexity: O(1)
23+
> head를 제외하고 아무 변수도 사용하지 않았으므로 O(1)
24+
"""
25+
def solve(self, head: Optional[ListNode]) -> bool:
26+
if not head:
27+
return False
28+
29+
while head.next:
30+
if head.next and head.next.val is None:
31+
return True
32+
33+
head.val = None
34+
head = head.next
35+
36+
return False
37+
38+
39+
class _LeetCodeTestCases(TestCase):
40+
def test_1(self):
41+
head = None
42+
output = False
43+
self.assertEqual(Solution().hasCycle(head), output)
44+
45+
46+
if __name__ == '__main__':
47+
main()

maximum-product-subarray/EGON.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
from typing import List
2+
from unittest import TestCase, main
3+
4+
5+
class Solution:
6+
def maxProduct(self, nums: List[int]) -> int:
7+
return self.solveWithDP(nums)
8+
9+
"""
10+
Runtime: 71 ms (Beats 61.13%)
11+
Time Complexity: O(n)
12+
- dp 배열 초기화를 위한 nums.copy()에 O(n)
13+
- range(1, L) 조회하며 조건에 따라 연산에 O(n - 1)
14+
- range(L) 조회하며 max 계산에 O(n)
15+
> O(n) + O(n - 1) + O(n) ~= O(n)
16+
17+
Memory: 17.75 MB (Beats 11.09%)
18+
Space Complexity: O(n)
19+
- 크기가 n인 배열 2개 사용했으므로 2 * O(n)
20+
> O(2n) ~= O(n)
21+
"""
22+
def solveWithDP(self, nums: List[int]) -> int:
23+
L = len(nums)
24+
forward_product, backward_product = nums.copy(), nums.copy()
25+
for i in range(1, L):
26+
if forward_product[i - 1] != 0:
27+
forward_product[i] *= forward_product[i - 1]
28+
29+
if backward_product[L - i] != 0:
30+
backward_product[L - i - 1] *= backward_product[L - i]
31+
32+
result = nums[0]
33+
for i in range(L):
34+
result = max(result, forward_product[i], backward_product[i])
35+
36+
return result
37+
38+
39+
class _LeetCodeTestCases(TestCase):
40+
def test_1(self):
41+
nums = [2,3,-2,4]
42+
output = 6
43+
self.assertEqual(Solution.maxProduct(Solution(), nums), output)
44+
45+
def test_2(self):
46+
nums = [-2,0,-1]
47+
output = 0
48+
self.assertEqual(Solution.maxProduct(Solution(), nums), output)
49+
50+
def test_3(self):
51+
nums = [-2]
52+
output = -2
53+
self.assertEqual(Solution.maxProduct(Solution(), nums), output)
54+
55+
def test_4(self):
56+
nums = [0,-3,-2,-3,-2,2,-3,0,1,-1]
57+
output = 72
58+
self.assertEqual(Solution.maxProduct(Solution(), nums), output)
59+
60+
def test_5(self):
61+
nums = [7, -2, -4]
62+
output = 56
63+
self.assertEqual(Solution.maxProduct(Solution(), nums), output)
64+
65+
66+
if __name__ == '__main__':
67+
main()

maximum-subarray/EGON.py

Lines changed: 60 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,64 +3,86 @@
33

44

55
class Solution:
6-
def maxProduct(self, nums: List[int]) -> int:
7-
return self.solveWithDP(nums)
6+
def maxSubArray(self, nums: List[int]) -> int:
7+
return self.solve_divide_and_conquer(nums)
88

99
"""
10-
Runtime: 71 ms (Beats 61.13%)
10+
Runtime: 548 ms (Beats 38.42%)
1111
Time Complexity: O(n)
12-
- dp 배열 초기화를 위한 nums.copy()에 O(n)
13-
- range(1, L) 조회하며 조건에 따라 연산에 O(n - 1)
14-
- range(L) 조회하며 max 계산에 O(n)
15-
> O(n) + O(n - 1) + O(n) ~= O(n)
12+
- nums를 조회하는데 O(n)
13+
- max_sum을 갱신하는데 2개 항에 대한 max연산에 O(2)
14+
- max_subarray_sum을 갱신하는데 2개 항에 대한 max 연산에 O(2)
15+
> O(n) * (O(2) + O(2)) = O(4 * n) ~= O(n)
1616
17-
Memory: 17.75 MB (Beats 11.09%)
17+
Memory: 30.96 MB (Beats 74.82%)
18+
Space Complexity: O(1)
19+
> 정수형 변수, 실수형 변수 하나 씩만 사용했으므로 O(1)
20+
"""
21+
def solve_kadane(self, nums: List[int]) -> int:
22+
max_subarray_sum, result = 0, float('-inf')
23+
for num in nums:
24+
max_subarray_sum = max(num, max_subarray_sum + num)
25+
result = max(max_subarray_sum, result)
26+
return result
27+
28+
"""
29+
Runtime: 732 ms (Beats 5.04%)
30+
Time Complexity: O(n * log n)
31+
- max_prefix_sum에서 deepcopy에 O(n), 계산에 O(n)
32+
- max_suffix_sum에서 deepcopy에 O(n), 계산에 O(n)
33+
- divide_and_sum에서 재귀 호출 depth가 log n, 호출 결과의 최대 갯수는 n이므로, 일반적인 divide and conquer의 시간복잡도와 동일한 O(n * log n)
34+
> 2 * O(n) + 2 * O(n) + O(n * log n) ~= O(n * log n)
35+
36+
Memory: 68.75 MB (Beats 20.29%)
1837
Space Complexity: O(n)
19-
- 크기가 n인 배열 2개 사용했으므로 2 * O(n)
20-
> O(2n) ~= O(n)
38+
- max_prefix_sum에서 O(n)
39+
- max_suffix_sum에서 O(n)
40+
> O(n) + O(n) = 2 * O(n) ~= O(n)
2141
"""
22-
def solveWithDP(self, nums: List[int]) -> int:
23-
L = len(nums)
24-
forward_product, backward_product = nums.copy(), nums.copy()
25-
for i in range(1, L):
26-
if forward_product[i - 1] != 0:
27-
forward_product[i] *= forward_product[i - 1]
42+
def solve_divide_and_conquer(self, nums: List[int]) -> int:
43+
max_prefix_sum = nums[::]
44+
for i in range(1, len(nums)):
45+
max_prefix_sum[i] = max(max_prefix_sum[i], max_prefix_sum[i - 1] + nums[i])
2846

29-
if backward_product[L - i] != 0:
30-
backward_product[L - i - 1] *= backward_product[L - i]
47+
max_suffix_sum = nums[::]
48+
for i in range(len(nums) - 2, -1, -1):
49+
max_suffix_sum[i] = max(max_suffix_sum[i], max_suffix_sum[i + 1] + nums[i])
3150

32-
result = nums[0]
33-
for i in range(L):
34-
result = max(result, forward_product[i], backward_product[i])
51+
def divide_and_sum(nums: List[int], left: int, right: int) -> int:
52+
if left == right:
53+
return nums[left]
3554

36-
return result
55+
mid = (left + right) // 2
56+
57+
return max(
58+
divide_and_sum(nums, left, mid),
59+
max_prefix_sum[mid] + max_suffix_sum[mid + 1],
60+
divide_and_sum(nums, mid + 1, right)
61+
)
62+
63+
return divide_and_sum(nums, 0, len(nums) - 1)
3764

3865

3966
class _LeetCodeTestCases(TestCase):
4067
def test_1(self):
41-
nums = [2,3,-2,4]
68+
nums = [-2,1,-3,4,-1,2,1,-5,4]
4269
output = 6
43-
self.assertEqual(Solution.maxProduct(Solution(), nums), output)
70+
self.assertEqual(Solution.maxSubArray(Solution(), nums), output)
4471

4572
def test_2(self):
46-
nums = [-2,0,-1]
47-
output = 0
48-
self.assertEqual(Solution.maxProduct(Solution(), nums), output)
73+
nums = [1]
74+
output = 1
75+
self.assertEqual(Solution.maxSubArray(Solution(), nums), output)
4976

5077
def test_3(self):
51-
nums = [-2]
52-
output = -2
53-
self.assertEqual(Solution.maxProduct(Solution(), nums), output)
78+
nums = [5,4,-1,7,8]
79+
output = 23
80+
self.assertEqual(Solution.maxSubArray(Solution(), nums), output)
5481

5582
def test_4(self):
56-
nums = [0,-3,-2,-3,-2,2,-3,0,1,-1]
57-
output = 72
58-
self.assertEqual(Solution.maxProduct(Solution(), nums), output)
59-
60-
def test_5(self):
61-
nums = [7, -2, -4]
62-
output = 56
63-
self.assertEqual(Solution.maxProduct(Solution(), nums), output)
83+
nums = [-4, -3, -2, -1]
84+
output = -1
85+
self.assertEqual(Solution.maxSubArray(Solution(), nums), output)
6486

6587

6688
if __name__ == '__main__':

minimum-window-substring/EGON.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
from collections import Counter
2+
from typing import List
3+
from unittest import TestCase, main
4+
5+
6+
class Solution:
7+
def minWindow(self, s: str, t: str) -> str:
8+
return self.solve_two_pointer(s, t)
9+
10+
"""
11+
Runtime: 129 ms (Beats 50.44%)
12+
Time Complexity: O(S)
13+
- 문자열 s를 enumerate로 순회하는데 O(S)
14+
- 순회 후 left를 갱신하는 while문에서 left가 0부터 n까지 단조증가하므로 총 조회는 O(S)
15+
> O(S) + O(S) ~= O(S)
16+
17+
Memory: 17.32 MB (Beats 32.52%)
18+
Space Complexity: O(S)
19+
- counter 변수의 초기 크기는 O(T)
20+
- 반복문을 조회하며 counter 갱신, 최악의 경우 s의 모든 문자가 다르고 s == t인 경우 이므로 O(S), upper bound
21+
> O(S)
22+
"""
23+
def solve_two_pointer(self, s: str, t: str) -> str:
24+
counter = Counter(t)
25+
missing = len(t)
26+
left = start = end = 0
27+
for right, char in enumerate(s, start=1):
28+
missing -= counter[char] > 0
29+
counter[char] -= 1
30+
31+
if missing == 0:
32+
while left < right and counter[s[left]] < 0:
33+
counter[s[left]] += 1
34+
left += 1
35+
36+
if not end or right - left <= end - start:
37+
start, end = left, right
38+
39+
counter[s[left]] += 1
40+
missing += 1
41+
left += 1
42+
43+
return s[start:end]
44+
45+
46+
class _LeetCodeTestCases(TestCase):
47+
def test_1(self):
48+
s = "ADOBECODEBANC"
49+
t = "ABC"
50+
output = "BANC"
51+
self.assertEqual(Solution.minWindow(Solution(), s, t), output)
52+
53+
def test_2(self):
54+
s = "a"
55+
t = "a"
56+
output = "a"
57+
self.assertEqual(Solution.minWindow(Solution(), s, t), output)
58+
59+
def test_3(self):
60+
s = "a"
61+
t = "aa"
62+
output = ""
63+
self.assertEqual(Solution.minWindow(Solution(), s, t), output)
64+
65+
66+
if __name__ == '__main__':
67+
main()

0 commit comments

Comments
 (0)