diff --git a/binary-tree-level-order-traversal/alphaorderly.py b/binary-tree-level-order-traversal/alphaorderly.py new file mode 100644 index 0000000000..07a532cc33 --- /dev/null +++ b/binary-tree-level-order-traversal/alphaorderly.py @@ -0,0 +1,38 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +""" +시간복잡도: O(n) + - n: 트리의 모든 노드 개수 (각 노드를 한 번씩 방문) +공간복잡도: O(n) + - n: 최악의 경우 큐(queue)에 저장되는 노드 수(가장 마지막 레벨의 노드 수와 동일) + +BFS(너비 우선 탐색)을 활용하여 트리를 레벨별로 순회하며, +각 레벨마다 노드의 값을 리스트로 모아 결과 리스트에 추가합니다. +""" +class Solution: + def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + + if not root: + return [] + + queue = deque([root]) + ans = [] + + while queue: + temp = [] + Q = len(queue) + for _ in range(Q): + node = queue.popleft() + temp.append(node.val) + + if node.left: + queue.append(node.left) + if node.right: + queue.append(node.right) + ans.append(temp) + + return ans diff --git a/counting-bits/alphaorderly.py b/counting-bits/alphaorderly.py new file mode 100644 index 0000000000..c1f27066f1 --- /dev/null +++ b/counting-bits/alphaorderly.py @@ -0,0 +1,14 @@ +""" +시간복잡도: O(n) + - n: 입력된 숫자 n까지의 범위 +공간복잡도: O(n) + - n: 결과값 리스트(ans)의 크기 +""" +class Solution: + def countBits(self, n: int) -> List[int]: + ans = [0] * (n + 1) + + for i in range(1, n + 1): + ans[i] = (i & 1) + ans[i // 2] + + return ans diff --git a/house-robber-ii/alphaorderly.py b/house-robber-ii/alphaorderly.py new file mode 100644 index 0000000000..aeb202a6bd --- /dev/null +++ b/house-robber-ii/alphaorderly.py @@ -0,0 +1,29 @@ +""" +시간복잡도: O(n) + - n: 입력된 배열 nums의 길이 +공간복잡도: O(n) + - n: DP 리스트(first, second)의 크기 + +집이 원형이라 첫 집과 마지막 집을 동시에 털 수 없으므로, +두 구간으로 나누어 각각 선형 House Robber를 적용합니다. + +first는 마지막 집을 제외한 구간(0 ~ n-2), +second는 첫 집을 제외한 구간(1 ~ n-1)의 최대 금액을 저장합니다. + +두 구간의 마지막 값 중 더 큰 값을 반환합니다. +""" +class Solution: + def rob(self, nums: List[int]) -> int: + + if len(nums) <= 2: + return max(nums) + + first = [nums[0], max(nums[0], nums[1])] + second = [0, nums[1]] + + for i in range(2, len(nums)): + if i != len(nums) - 1: + first.append(max(nums[i] + first[i - 2], first[i - 1])) + second.append(max(nums[i] + second[i - 2], second[i - 1])) + + return max(first[-1], second[-1]) diff --git a/meeting-rooms-ii/alphaorderly.py b/meeting-rooms-ii/alphaorderly.py new file mode 100644 index 0000000000..27a7e39285 --- /dev/null +++ b/meeting-rooms-ii/alphaorderly.py @@ -0,0 +1,27 @@ +""" +시간복잡도: O(n log n) + - n: 입력된 배열 intervals의 길이 +공간복잡도: O(n) + - n: 진행 중인 회의의 종료 시각을 저장하는 최소 힙의 크기 + +intervals를 시작 시각 기준으로 정렬합니다. +최소 힙에는 아직 사용 중인 회의실의 종료 시각을 저장합니다. + +각 회의의 시작 시각이 힙의 가장 이른 종료 시각 이후이면 +그 회의실은 비었으므로 힙에서 제거하고, +현재 회의의 종료 시각을 힙에 넣습니다. +힙 크기의 최댓값이 필요한 최소 회의실 개수입니다. +""" +class Solution: + def minMeetingRooms(self, intervals: List[List[int]]) -> int: + intervals.sort() + ans = 1 + heap = [] + + for start, end in intervals: + while heap and heap[0] <= start: + heappop(heap) + heappush(heap, end) + ans = max(ans, len(heap)) + + return ans diff --git a/word-search-ii/alphaorderly.py b/word-search-ii/alphaorderly.py new file mode 100644 index 0000000000..e3ba276fc3 --- /dev/null +++ b/word-search-ii/alphaorderly.py @@ -0,0 +1,83 @@ +""" +시간복잡도: O(w + m * n * 4^l) + - w: 모든 단어 길이의 합 (트라이 구성) + - m, n: 보드의 행, 열 + - l: 단어의 최대 길이 (각 칸에서 상하좌우 DFS) +공간복잡도: O(w + l) + - w: 트라이와 정답 집합 + - l: DFS 재귀 깊이와 현재 경로의 방문 집합 + +단어를 트라이에 넣은 뒤, 보드의 각 칸에서 DFS로 이어지는 단어를 찾습니다. +이미 찾은 단어는 is_end를 끄고, 더 이상 이어지지 않는 노드는 트라이에서 지워 +같은 접두사를 다시 탐색하지 않습니다. +""" +class TrieNode: + def __init__(self): + self.is_end = False + self.children = dict() + + def add_word(self, word: str): + node = self + + for ch in word: + if ch not in node.children: + node.children[ch] = TrieNode() + node = node.children[ch] + + # 이 글자에서 단어가 끝난다 + node.is_end = True + + +class Solution: + def findWords(self, board: list[list[str]], words: list[str]) -> list[str]: + ROW, COL = len(board), len(board[0]) + # 상하좌우 + DIR = [[1, 0], [0, 1], [-1, 0], [0, -1]] + + trie = TrieNode() + for word in words: + trie.add_word(word) + + def bound(row: int, col: int) -> bool: + return 0 <= row < ROW and 0 <= col < COL + + # ans: 찾은 단어, visited: 현재 경로에서 지난 칸 + ans, visited = set(), set() + + def search(r: int, c: int, node: TrieNode, word: str): + # 다음 글자가 트라이에 없거나, 같은 칸을 다시 밟으면 중단 + if board[r][c] not in node.children or (r, c) in visited: + return + + visited.add((r, c)) + + # 가지치기할 때 부모에서 현재 글자를 지우기 위해 보관 + parent = node + ch = board[r][c] + + node = node.children[ch] + word += ch + + if node.is_end: + ans.add(word) + # 같은 단어는 한 번만 찾는다 + node.is_end = False + + for dr, dc in DIR: + tr, tc = r + dr, c + dc + if not bound(tr, tc): + continue + search(tr, tc, node, word) + + # 백트래킹: 이 칸을 다른 경로에서 다시 쓸 수 있게 되돌린다 + visited.remove((r, c)) + + # 자식도 없고 여기서 끝나는 단어도 없으면 트라이에서 제거 + if not node.children and not node.is_end: + del parent.children[ch] + + for r in range(ROW): + for c in range(COL): + search(r, c, trie, "") + + return list(ans)