Skip to content
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

[EGON] Week9 Solutions #525

Merged
merged 6 commits into from
Oct 12, 2024
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
43 changes: 43 additions & 0 deletions find-minimum-in-rotated-sorted-array/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from typing import List
from unittest import TestCase, main


class Solution:
def findMin(self, nums: List[int]) -> int:
return self.solve_binary_search(nums)

"""
Runtime: 32 ms (Beats 97.56%)
Time Complexity: O(log n)
- 크기가 n인 배열에 대한 이분탐색에 O(log n)
- while 조건문 판단에 O(2), and 연산이므로 단축 평가에 의해 upper bound
- 엣지 케이스 처리를 위한 마지막 lo, hi 2개 항에 대한 min연산에 O(2)
> O(log n) * O(2) + O(2) ~= O(log n)

Memory: 16.82 (Beats 50.00%)
Space Complexity: O(1)
> 이분탐색에 필요한 정수형 변수 lo, hi, mid 3개만 사용했으므로 n과 상관없이 O(1)
"""
def solve_binary_search(self, nums: List[int]) -> int:
lo, hi = 0, len(nums) - 1
while lo < hi and nums[hi] < nums[lo]:
mid = (lo + hi) // 2
if nums[lo] < nums[mid]:
lo = mid
elif nums[mid] < nums[hi]:
hi = mid
else:
break

return min(nums[lo], nums[hi])


class _LeetCodeTestCases(TestCase):
def test_1(self):
nums = [2, 1]
output = 1
self.assertEqual(Solution().findMin(nums), output)


if __name__ == '__main__':
main()
47 changes: 47 additions & 0 deletions linked-list-cycle/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from typing import Optional
from unittest import TestCase, main


# Definition for singly-linked list.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None


class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
return self.solve(head)

"""
Runtime: 37 ms (Beats 93.02%)
Time Complexity: O(n)
> head부터 next가 있는 동안 선형적으로 조회하므로 O(n)

Memory: 18.62 (Beats 98.22%)
Space Complexity: O(1)
> head를 제외하고 아무 변수도 사용하지 않았으므로 O(1)
"""
def solve(self, head: Optional[ListNode]) -> bool:
if not head:
return False

while head.next:
if head.next and head.next.val is None:
return True

head.val = None
head = head.next

return False


class _LeetCodeTestCases(TestCase):
def test_1(self):
head = None
output = False
self.assertEqual(Solution().hasCycle(head), output)


if __name__ == '__main__':
main()
67 changes: 67 additions & 0 deletions maximum-product-subarray/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from typing import List
from unittest import TestCase, main


class Solution:
def maxProduct(self, nums: List[int]) -> int:
return self.solveWithDP(nums)

"""
Runtime: 71 ms (Beats 61.13%)
Time Complexity: O(n)
- dp 배열 초기화를 위한 nums.copy()에 O(n)
- range(1, L) 조회하며 조건에 따라 연산에 O(n - 1)
- range(L) 조회하며 max 계산에 O(n)
> O(n) + O(n - 1) + O(n) ~= O(n)

Memory: 17.75 MB (Beats 11.09%)
Space Complexity: O(n)
- 크기가 n인 배열 2개 사용했으므로 2 * O(n)
> O(2n) ~= O(n)
"""
def solveWithDP(self, nums: List[int]) -> int:
L = len(nums)
forward_product, backward_product = nums.copy(), nums.copy()
for i in range(1, L):
if forward_product[i - 1] != 0:
forward_product[i] *= forward_product[i - 1]

if backward_product[L - i] != 0:
backward_product[L - i - 1] *= backward_product[L - i]

result = nums[0]
for i in range(L):
result = max(result, forward_product[i], backward_product[i])

return result


class _LeetCodeTestCases(TestCase):
def test_1(self):
nums = [2,3,-2,4]
output = 6
self.assertEqual(Solution.maxProduct(Solution(), nums), output)

def test_2(self):
nums = [-2,0,-1]
output = 0
self.assertEqual(Solution.maxProduct(Solution(), nums), output)

def test_3(self):
nums = [-2]
output = -2
self.assertEqual(Solution.maxProduct(Solution(), nums), output)

def test_4(self):
nums = [0,-3,-2,-3,-2,2,-3,0,1,-1]
output = 72
self.assertEqual(Solution.maxProduct(Solution(), nums), output)

def test_5(self):
nums = [7, -2, -4]
output = 56
self.assertEqual(Solution.maxProduct(Solution(), nums), output)


if __name__ == '__main__':
main()
98 changes: 60 additions & 38 deletions maximum-subarray/EGON.py
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maximum-product-subarray 파일이 여기에 잘못 들어있었어서 수정했습니다. code diff 때문에 보기 힘드실텐데, 오른쪽의 "View file"을 통해 파일 단위로 리뷰 부탁드립니다.

Original file line number Diff line number Diff line change
Expand Up @@ -3,64 +3,86 @@


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

"""
Runtime: 71 ms (Beats 61.13%)
Runtime: 548 ms (Beats 38.42%)
Time Complexity: O(n)
- dp 배열 초기화를 위한 nums.copy()에 O(n)
- range(1, L) 조회하며 조건에 따라 연산에 O(n - 1)
- range(L) 조회하며 max 계산에 O(n)
> O(n) + O(n - 1) + O(n) ~= O(n)
- nums를 조회하는데 O(n)
- max_sum을 갱신하는데 2개 항에 대한 max연산에 O(2)
- max_subarray_sum을 갱신하는데 2개 항에 대한 max 연산에 O(2)
> O(n) * (O(2) + O(2)) = O(4 * n) ~= O(n)

Memory: 17.75 MB (Beats 11.09%)
Memory: 30.96 MB (Beats 74.82%)
Space Complexity: O(1)
> 정수형 변수, 실수형 변수 하나 씩만 사용했으므로 O(1)
"""
def solve_kadane(self, nums: List[int]) -> int:
max_subarray_sum, result = 0, float('-inf')
for num in nums:
max_subarray_sum = max(num, max_subarray_sum + num)
result = max(max_subarray_sum, result)
return result

"""
Runtime: 732 ms (Beats 5.04%)
Time Complexity: O(n * log n)
- max_prefix_sum에서 deepcopy에 O(n), 계산에 O(n)
- max_suffix_sum에서 deepcopy에 O(n), 계산에 O(n)
- divide_and_sum에서 재귀 호출 depth가 log n, 호출 결과의 최대 갯수는 n이므로, 일반적인 divide and conquer의 시간복잡도와 동일한 O(n * log n)
> 2 * O(n) + 2 * O(n) + O(n * log n) ~= O(n * log n)

Memory: 68.75 MB (Beats 20.29%)
Space Complexity: O(n)
- 크기가 n인 배열 2개 사용했으므로 2 * O(n)
> O(2n) ~= O(n)
- max_prefix_sum에서 O(n)
- max_suffix_sum에서 O(n)
> O(n) + O(n) = 2 * O(n) ~= O(n)
"""
def solveWithDP(self, nums: List[int]) -> int:
L = len(nums)
forward_product, backward_product = nums.copy(), nums.copy()
for i in range(1, L):
if forward_product[i - 1] != 0:
forward_product[i] *= forward_product[i - 1]
def solve_divide_and_conquer(self, nums: List[int]) -> int:
max_prefix_sum = nums[::]
for i in range(1, len(nums)):
max_prefix_sum[i] = max(max_prefix_sum[i], max_prefix_sum[i - 1] + nums[i])

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

result = nums[0]
for i in range(L):
result = max(result, forward_product[i], backward_product[i])
def divide_and_sum(nums: List[int], left: int, right: int) -> int:
if left == right:
return nums[left]

return result
mid = (left + right) // 2

return max(
divide_and_sum(nums, left, mid),
max_prefix_sum[mid] + max_suffix_sum[mid + 1],
divide_and_sum(nums, mid + 1, right)
)

return divide_and_sum(nums, 0, len(nums) - 1)


class _LeetCodeTestCases(TestCase):
def test_1(self):
nums = [2,3,-2,4]
nums = [-2,1,-3,4,-1,2,1,-5,4]
output = 6
self.assertEqual(Solution.maxProduct(Solution(), nums), output)
self.assertEqual(Solution.maxSubArray(Solution(), nums), output)

def test_2(self):
nums = [-2,0,-1]
output = 0
self.assertEqual(Solution.maxProduct(Solution(), nums), output)
nums = [1]
output = 1
self.assertEqual(Solution.maxSubArray(Solution(), nums), output)

def test_3(self):
nums = [-2]
output = -2
self.assertEqual(Solution.maxProduct(Solution(), nums), output)
nums = [5,4,-1,7,8]
output = 23
self.assertEqual(Solution.maxSubArray(Solution(), nums), output)

def test_4(self):
nums = [0,-3,-2,-3,-2,2,-3,0,1,-1]
output = 72
self.assertEqual(Solution.maxProduct(Solution(), nums), output)

def test_5(self):
nums = [7, -2, -4]
output = 56
self.assertEqual(Solution.maxProduct(Solution(), nums), output)
nums = [-4, -3, -2, -1]
output = -1
self.assertEqual(Solution.maxSubArray(Solution(), nums), output)


if __name__ == '__main__':
Expand Down
67 changes: 67 additions & 0 deletions minimum-window-substring/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from collections import Counter
from typing import List
from unittest import TestCase, main


class Solution:
def minWindow(self, s: str, t: str) -> str:
return self.solve_two_pointer(s, t)

"""
Runtime: 129 ms (Beats 50.44%)
Time Complexity: O(S)
- 문자열 s를 enumerate로 순회하는데 O(S)
- 순회 후 left를 갱신하는 while문에서 left가 0부터 n까지 단조증가하므로 총 조회는 O(S)
> O(S) + O(S) ~= O(S)

Memory: 17.32 MB (Beats 32.52%)
Space Complexity: O(S)
- counter 변수의 초기 크기는 O(T)
- 반복문을 조회하며 counter 갱신, 최악의 경우 s의 모든 문자가 다르고 s == t인 경우 이므로 O(S), upper bound
> O(S)
"""
def solve_two_pointer(self, s: str, t: str) -> str:
counter = Counter(t)
missing = len(t)
left = start = end = 0
for right, char in enumerate(s, start=1):
missing -= counter[char] > 0
counter[char] -= 1

if missing == 0:
while left < right and counter[s[left]] < 0:
counter[s[left]] += 1
left += 1

if not end or right - left <= end - start:
start, end = left, right

counter[s[left]] += 1
missing += 1
left += 1

return s[start:end]


class _LeetCodeTestCases(TestCase):
def test_1(self):
s = "ADOBECODEBANC"
t = "ABC"
output = "BANC"
self.assertEqual(Solution.minWindow(Solution(), s, t), output)

def test_2(self):
s = "a"
t = "a"
output = "a"
self.assertEqual(Solution.minWindow(Solution(), s, t), output)

def test_3(self):
s = "a"
t = "aa"
output = ""
self.assertEqual(Solution.minWindow(Solution(), s, t), output)


if __name__ == '__main__':
main()
Loading