Skip to content

[EGON] Week 6 Solutions #467

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

Merged
merged 2 commits into from
Sep 18, 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
47 changes: 47 additions & 0 deletions container-with-most-water/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from typing import List
from unittest import TestCase, main


class Solution:
def maxArea(self, height: List[int]) -> int:
return self.solveWithTwoPointer(height)

"""
Runtime: 527 ms (Beats 47.12%)
Time Complexity: O(n)
- 투 포인터의 총합 조회 범위가 height의 길이와 같으므로 O(n)
- area 갱신을 위한 계산에서 항이 2개인 max와 항이 2개인 min 중첩에 O(2) * O(2)
> O(n) * O(4) ~= O(n)

Memory: 29.61 MB (Beats 38.93%)
Space Complexity: O(1)
> 정수형 변수들만 사용했으므로 O(1)
"""
def solveWithTwoPointer(self, height: List[int]) -> int:
left, right = 0, len(height) - 1
area = 0
while left < right:
area = max(area, (right - left) * min(height[right], height[left]))

if height[left] <= height[right]:
left += 1
else:
right -= 1

return area


class _LeetCodeTestCases(TestCase):
def test_1(self):
height = [1, 8, 6, 2, 5, 4, 8, 3, 7]
output = 49
self.assertEqual(Solution.maxArea(Solution(), height), output)

def test_2(self):
height = [1, 1]
output = 1
self.assertEqual(Solution.maxArea(Solution(), height), output)


if __name__ == '__main__':
main()
86 changes: 86 additions & 0 deletions design-add-and-search-words-data-structure/EGON.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from unittest import TestCase, main


class Node:

def __init__(self, key):
self.key = key
self.isWordEnd = False
self.children = {}


class Trie:
WILD_CARD = '.'

def __init__(self):
self.root = Node(None)

def insert(self, word: str) -> None:
curr_node = self.root
for char in word:
if char not in curr_node.children:
curr_node.children[char] = Node(char)

curr_node = curr_node.children[char]
curr_node.isWordEnd = True

def search(self, node: Node, word: str, idx: int) -> bool:
if idx == len(word):
return node.isWordEnd

for idx in range(idx, len(word)):
if word[idx] == self.WILD_CARD:
for child in node.children.values():
if self.search(child, word, idx + 1) is True:
return True
else:
return False

if word[idx] in node.children:
return self.search(node.children[word[idx]], word, idx + 1)
else:
return False


"""
Runtime: 1810 ms (Beats 22.46%)
Time Complexity:
> addWord: word의 길이만큼 순회하므로 O(L)
> search:
- word의 평균 길이를 W이라하면,
- '.'가 포함되어 있지 않는 경우 O(W), early return 가능하므로 upper bound
- '.'가 포함되어 있는 경우, 해당 노드의 child만큼 재귀, trie의 평균 자식 수를 C라 하면 O(W) * O(C), early return 가능하므로 upper bound
- trie의 평균 자식 수는 addWord의 실행횟수 C'에 선형적으로 비레(겹치는 char가 없는 경우 upper bound)
> O(W) * O(C) ~= O(W) * O(C') ~= O(W * C'), upper bound

Memory: 66.78 MB (Beats 12.26%)
Space Complexity: O(1)
> addWord:
- 삽입한 word의 평균 길이 L만큼 Node가 생성 및 Trie에 추가, O(L)
- addWord의 실행횟수 C'에 비례, O(C')
> O(L) * O(C') ~= O(L * C')
> search:
> 만들어진 Trie와 패러미터 word, 정수 변수 idx를 사용하므로 O(1)

"""
class WordDictionary:

def __init__(self):
self.trie = Trie()

def addWord(self, word: str) -> None:
self.trie.insert(word)

def search(self, word: str) -> bool:
return self.trie.search(self.trie.root, word, 0)


class _LeetCodeTestCases(TestCase):
def test_1(self):
wordDictionary = WordDictionary()
wordDictionary.addWord("at")
self.assertEqual(wordDictionary.search(".at"), False)


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


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

"""
Runtime: 68 ms (Beats 86.42%)
Time Complexity: O(n * log n)
- nums 배열 조회에 O(n)
- 최악의 경우 num의 모든 원소에 대해 bisect_left 실행가능, O(log n) upper bound
> O(n) * O(log n) ~= O(n * log n)

Memory: 16.92 MB (Beats 29.49%)
Space Complexity: O(n)
> 최대 크기가 n인 lis 배열 사용에 O(n)
"""
def solve_with_Memo_BS(self, nums: List[int]) -> int:

def bisect_left(lis: List[int], target: int):
lo, hi = 0, len(lis)
while lo < hi:
mid = lo + (hi - lo) // 2
if lis[mid] < target:
lo = mid + 1
else:
hi = mid

return lo

lis = []
for num in nums:
if not lis:
lis.append(num)
continue

if lis[-1] < num:
lis.append(num)
else:
lis[bisect_left(lis, num)] = num

return len(lis)


class _LeetCodeTestCases(TestCase):
def test_1(self):
prices = [10,9,2,5,3,7,101,18]
output = 4
self.assertEqual(Solution.lengthOfLIS(Solution(), prices), output)


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


class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
return self.solve(matrix)

"""
Runtime: 37 ms (Beats 44.53%)
Time Complexity: O(MAX_R * MAX_C)

Memory: 16.56 MB (Beats 43.42%)
Space Complexity: O(1)
- result를 제외하고 matrix의 값을 변경해서 visited 분기
"""
def solve(self, matrix: List[List[int]]) -> List[int]:
MAX_R, MAX_C = len(matrix), len(matrix[0])
DIRS = ((0, 1), (1, 0), (0, -1), (-1, 0))
result = []
r, c, dir_idx = 0, -1, 0
for _ in range(MAX_R * MAX_C):
r += DIRS[dir_idx][0]
c += DIRS[dir_idx][1]

if 0 <= r < MAX_R and 0 <= c < MAX_C and matrix[r][c] is not None:
result.append(matrix[r][c])
matrix[r][c] = None
else:
r -= DIRS[dir_idx][0]
c -= DIRS[dir_idx][1]
dir_idx = (dir_idx + 1) % len(DIRS)
r += DIRS[dir_idx][0]
c += DIRS[dir_idx][1]
result.append(matrix[r][c])
matrix[r][c] = None

return result


class _LeetCodeTestCases(TestCase):
def test_1(self):
matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
output = [1,2,3,4,8,12,11,10,9,5,6,7]
self.assertEqual(Solution.spiralOrder(Solution(), matrix), output)


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


class Solution:
def isValid(self, s: str) -> bool:
return self.solveWithStack(s)

"""
Runtime: 34 ms (Beats 66.54%)
Time Complexity: O(n)
> 문자열 s의 길이 L만큼 조회하므로 O(n), early return이 있으므로 upper bound

Memory: 16.59 MB (Beats 50.97%)
Space Complexity: O(n)
- close_p_dict의 크기는 상수로 처리
> 최대 길이가 L인 배열 stack을 사용했으므로 O(n)
"""
def solveWithStack(self, s: str) -> bool:
close_p_dict = {')': '(', '}': '{', ']': '['}
stack = []
for curr_p in s:
if not stack or curr_p not in close_p_dict:
stack.append(curr_p)
continue

if close_p_dict[curr_p] != stack[-1]:
return False
else:
stack.pop()

return not stack


class _LeetCodeTestCases(TestCase):
def test_1(self):
s = "()"
output = True
self.assertEqual(Solution.isValid(Solution(), s), output)

def test_2(self):
s = "()[]{}"
output = True
self.assertEqual(Solution.isValid(Solution(), s), output)

def test_3(self):
s = "(]"
output = False
self.assertEqual(Solution.isValid(Solution(), s), output)

def test_4(self):
s = "([])"
output = True
self.assertEqual(Solution.isValid(Solution(), s), output)


if __name__ == '__main__':
main()