diff --git a/container-with-most-water/EGON.py b/container-with-most-water/EGON.py new file mode 100644 index 000000000..4aa2ea1dd --- /dev/null +++ b/container-with-most-water/EGON.py @@ -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() diff --git a/design-add-and-search-words-data-structure/EGON.py b/design-add-and-search-words-data-structure/EGON.py new file mode 100644 index 000000000..e5d1c1cef --- /dev/null +++ b/design-add-and-search-words-data-structure/EGON.py @@ -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() diff --git a/longest-increasing-subsequence/EGON.py b/longest-increasing-subsequence/EGON.py new file mode 100644 index 000000000..2cb73c056 --- /dev/null +++ b/longest-increasing-subsequence/EGON.py @@ -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) + - 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() diff --git a/spiral-matrix/EGON.py b/spiral-matrix/EGON.py new file mode 100644 index 000000000..65c627e77 --- /dev/null +++ b/spiral-matrix/EGON.py @@ -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() diff --git a/valid-parentheses/EGON.py b/valid-parentheses/EGON.py new file mode 100644 index 000000000..118ed41bc --- /dev/null +++ b/valid-parentheses/EGON.py @@ -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()