diff --git a/container-with-most-water/seungriyou.py b/container-with-most-water/seungriyou.py new file mode 100644 index 000000000..4a64715fd --- /dev/null +++ b/container-with-most-water/seungriyou.py @@ -0,0 +1,38 @@ +# https://leetcode.com/problems/container-with-most-water/ + +from typing import List + +class Solution: + def maxArea(self, height: List[int]) -> int: + """ + [Complexity] + - TC: O(n) + - SC: O(1) + + [Approach] + 양끝에서부터 범위를 좁히면서 확인하는 two-pointer를 이용한다. + 양끝 side 중 더 작은 쪽을 안쪽으로 한 칸 옮기면서 확인하고, + 만약 두 side가 길이가 같다면 두 개를 모두 안쪽으로 한 칸 옮긴다. + """ + + lo, hi = 0, len(height) - 1 + max_amount = 0 + + while lo < hi: + # 양 끝 side 길이 + s1, s2 = height[lo], height[hi] + + # max_amount 업데이트 + max_amount = max(max_amount, (hi - lo) * min(s1, s2)) + + # 더 낮은 side를 안쪽으로 한 칸 옮기기 + if s1 < s2: + lo += 1 + elif s1 > s2: + hi -= 1 + # 두 side의 크기가 같다면, 둘다 안쪽으로 한 칸씩 옮기기 + else: + lo += 1 + hi -= 1 + + return max_amount diff --git a/design-add-and-search-words-data-structure/seungriyou.py b/design-add-and-search-words-data-structure/seungriyou.py new file mode 100644 index 000000000..3bba4cbe5 --- /dev/null +++ b/design-add-and-search-words-data-structure/seungriyou.py @@ -0,0 +1,79 @@ +# https://leetcode.com/problems/design-add-and-search-words-data-structure/ + +from collections import defaultdict, deque + +class TrieNode: + def __init__(self): + self.is_word = False + self.children = defaultdict(TrieNode) + +class WordDictionary: + def __init__(self): + self.root = TrieNode() + + def addWord(self, word: str) -> None: + curr = self.root + + for w in word: + curr = curr.children[w] + + curr.is_word = True + + def search(self, word: str) -> bool: + """ + BFS 혹은 DFS를 사용할 수 있다. 핵심은 + - "."을 마주치면 모든 children을 전부 타고 내려간다. + - "."이 아닌 문자를 확인해야 한다면 해당 문자가 children에 있는지 확인 후 타고 내려간다. + 는 것이다. + """ + # return self._bfs(word) + return self._dfs(self.root, word, 0) + + def _dfs(self, curr, word, idx): + # base condition + if idx == len(word): + return curr.is_word + + # recur + # (1) "."을 마주치면 모든 children에 대해 타고 내려간다. + if word[idx] == ".": + for child in curr.children.values(): + if self._dfs(child, word, idx + 1): + return True + + # (2) 현재 idx의 문자가 children에 있는지 확인 후에 타고 내려간다. + if (w := word[idx]) in curr.children: + return self._dfs(curr.children[w], word, idx + 1) + + # (3) 그 외의 경우, False를 반환한다. + return False + + def _bfs(self, word): + q = deque([(self.root, 0)]) # (node, idx) + + while q: + curr, idx = q.popleft() + + # base condition + if idx == len(word): + if curr.is_word: + return True + + # iter + # (1) "."을 마주치면 모든 children에 대해 타고 내려간다. + elif word[idx] == ".": + for child in curr.children.values(): + q.append((child, idx + 1)) + + # (2) 현재 idx의 문자가 children에 있는지 확인 후에 타고 내려간다. + else: + if (w := word[idx]) in curr.children: + q.append((curr.children[w], idx + 1)) + + # (3) 그 외의 경우, False를 반환한다. + return False + +# Your WordDictionary object will be instantiated and called as such: +# obj = WordDictionary() +# obj.addWord(word) +# param_2 = obj.search(word) diff --git a/longest-increasing-subsequence/seungriyou.py b/longest-increasing-subsequence/seungriyou.py new file mode 100644 index 000000000..f166ee603 --- /dev/null +++ b/longest-increasing-subsequence/seungriyou.py @@ -0,0 +1,62 @@ +# https://leetcode.com/problems/longest-increasing-subsequence/ + +from typing import List + +class Solution: + def lengthOfLIS_dp(self, nums: List[int]) -> int: + """ + [Complexity] + - TC: O(n^2) + - SC: O(n) + + [Approach] + dp[i] = nums[i]가 포함되는 LIS의 최대 길이 + """ + n = len(nums) + dp = [1] * n + + for i in range(n): + for j in range(i): + if nums[j] < nums[i]: + dp[i] = max(dp[i], dp[j] + 1) + + return max(dp) + + def lengthOfLIS(self, nums: List[int]) -> int: + """ + [Complexity] + - TC: O(nlogn) (w. binary search) + - SC: O(n) + + [Approach] + ref: https://leetcode.com/problems/longest-increasing-subsequence/solutions/1326308/c-python-dp-binary-search-bit-segment-tree-solutions-picture-explain-o-nlogn + nums를 순차적으로 순회하며 LIS를 모은다. 이때, + - 현재 보고 있는 원소 n이 LIS의 마지막 원소 이하라면: LIS의 원소 중 (1) n 이상이면서 (2) 최솟값의 위치에 n 덮어쓰기 + - 그 외의 경우라면: LIS의 맨 끝에 num append + 한다. + LIS는 정렬되어 있을 것이므로 binary search를 이용할 수 있으며, 이렇게 구성한 LIS의 길이가 최대 길이이다. + """ + + def find_leftmost_idx(lis, n): + lo, hi = 0, len(lis) - 1 + + while lo < hi: + mid = lo + (hi - lo) // 2 + if lis[mid] < n: # -- 아예 제외하는 경우: n 미만인 경우 + lo = mid + 1 + else: + hi = mid + + return lo + + lis = [] + + for n in nums: + # 현재 보고 있는 n이 LIS의 마지막 원소 이하라면 + if lis and n <= lis[-1]: + lis[find_leftmost_idx(lis, n)] = n + # 그 외의 경우라면 + else: + lis.append(n) + + return len(lis) diff --git a/spiral-matrix/seungriyou.py b/spiral-matrix/seungriyou.py new file mode 100644 index 000000000..adb0cf02c --- /dev/null +++ b/spiral-matrix/seungriyou.py @@ -0,0 +1,41 @@ +# https://leetcode.com/problems/spiral-matrix/ + +from typing import List + +class Solution: + def spiralOrder(self, matrix: List[List[int]]) -> List[int]: + """ + [Complexity] + - TC: O(m * n) (m = row, n = col) + - SC: O(1) (결과용 배열 res 제외) + + [Approach] + 시계 방향으로 순회하도록, 방문 표시 & 방향 전환을 하면서 res에 값을 저장한다. + 이때, 방문 표시를 inplace로 한다면 추가 공간을 사용하지 않을 수 있다. + """ + + # 방향: 우 -> 하 -> 좌 -> 상 + dr, dc = [0, 1, 0, -1], [1, 0, -1, 0] + row, col = len(matrix), len(matrix[0]) + + r, c, d = 0, 0, 0 + res = [matrix[r][c]] + matrix[r][c] = "." + + while len(res) < row * col: + # 다음 위치 후보 구하기 + nr, nc = r + dr[d], c + dc[d] + + # 다음 위치 후보가 (1) 범위를 벗어났거나 (2) 이미 방문한 위치라면, 방향 틀기 + if not (0 <= nr < row and 0 <= nc < col) or matrix[nr][nc] == ".": + d = (d + 1) % 4 + nr, nc = r + dr[d], c + dc[d] + + # res에 추가 & 방문 표시 + res.append(matrix[nr][nc]) + matrix[nr][nc] = "." + + # 이동 + r, c = nr, nc + + return res diff --git a/valid-parentheses/seungriyou.py b/valid-parentheses/seungriyou.py new file mode 100644 index 000000000..7d84332a3 --- /dev/null +++ b/valid-parentheses/seungriyou.py @@ -0,0 +1,35 @@ +# https://leetcode.com/problems/valid-parentheses/ + +class Solution: + def isValid(self, s: str) -> bool: + """ + [Complexity] + - TC: O(n) + - SC: O(n) + + [Approach] + s를 순회하면서 다음을 stack에 수행한다. + - 닫는 괄호: stack이 비어있거나, stack.top이 매칭이 안 된다면 빠르게 False 반환 (+ 매칭이 된다면 stack.pop()) + - 여는 괄호: stack에 추가 + 이렇게 전체를 순회했을 때 stack이 비어있어야 valid parentheses이다. + """ + + matches = { + ")": "(", + "}": "{", + "]": "[" + } + + stack = [] + + for p in s: + # 닫는 괄호: (1) stack이 비어있거나 (2) stack의 top이 pair가 아니라면 False (+ pair라면 stack.pop()) + if p in matches: + if not stack or stack.pop() != matches[p]: + return False + # 여는 괄호: stack에 추가 + else: + stack.append(p) + + # stack이 비어있어야 valid parentheses + return not stack