diff --git a/alien-dictionary/alphaorderly.py b/alien-dictionary/alphaorderly.py new file mode 100644 index 0000000000..f85997afc9 --- /dev/null +++ b/alien-dictionary/alphaorderly.py @@ -0,0 +1,54 @@ +class Solution: + def alienOrder(self, words: List[str]) -> str: + # 단어 개수 저장 + N = len(words) + + # 그래프와 진입차수 저장 구조 초기화 + graph = defaultdict(list) + indegree = defaultdict(int) + + # 단어 쌍을 차례로 비교하며 그래프와 진입차수 정보 구축 + for i in range(1, N): + prev = words[i - 1] + curr = words[i] + + # 예외: 앞 단어가 더 길고, 뒷 단어의 접두사라면 잘못된 사전 순서임 + if len(prev) > len(curr) and prev.startswith(curr): + return "" + + # 첫 번째로 다른 글자 쌍 찾아서 순서 규칙 추가 + for prev_char, curr_char in zip(prev, curr): + if prev_char != curr_char: + if curr_char not in graph[prev_char]: # 중복 방지 + graph[prev_char].append(curr_char) + indegree[curr_char] += 1 + break # 한 쌍만 비교 + + # 등장하는 모든 글자 모음 + characters = set("".join(words)) + + # 진입차수 0인 글자를 큐에 넣어 시작 노드로 설정 + starts = deque([]) + for char in characters: + if indegree[char] == 0: + starts.append(char) + + ans = [] + + # 위상정렬(BFS) 진행 + while starts: + node = starts.popleft() + ans.append(node) + + # 현재 글자가 가리키는 다음 글자들의 진입차수 감소 및 0이 되면 큐에 추가 + for dest in graph[node]: + indegree[dest] -= 1 + if indegree[dest] == 0: + starts.append(dest) + + # 모든 글자를 위상정렬 결과에 포함 못하면 순환이 존재 → 빈 문자열 반환 + if len(ans) != len(characters): + return "" + + # 위상정렬 결과를 문자열로 반환 + return "".join(ans) diff --git a/construct-binary-tree-from-preorder-and-inorder-traversal/alphaorderly.py b/construct-binary-tree-from-preorder-and-inorder-traversal/alphaorderly.py new file mode 100644 index 0000000000..8553f478c5 --- /dev/null +++ b/construct-binary-tree-from-preorder-and-inorder-traversal/alphaorderly.py @@ -0,0 +1,38 @@ +""" +시간복잡도: O(n) +공간복잡도: O(n) + +- inorder 배열의 값과 인덱스를 딕셔너리로 저장하여 검색을 빠르게 함 +- preorder 배열을 순서대로 순회하며 각 노드의 값을 선택함 (루트부터 하위 트리 순) +- 선택된 값을 기준으로 inorder에서 왼쪽 구간(왼쪽 서브트리의 노드 개수), 오른쪽 구간을 계산 +- preorder, inorder 구간의 인덱스를 재귀적으로 갱신하여 왼쪽, 오른쪽 서브트리를 구성 +- 전체 트리를 재귀적으로 구성하며 그 결과를 반환 +""" +class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: + + pos = {k: v for v, k in enumerate(inorder)} + + def build( + pre: Tuple[int, int], ino: Tuple[int, int] + ) -> Optional[TreeNode]: + if pre[0] > pre[1]: + return None + + root = TreeNode(preorder[pre[0]]) + center = pos[preorder[pre[0]]] + + left_count = center - ino[0] + + root.left = build( + (pre[0] + 1, pre[0] + left_count), + (ino[0], ino[0] + left_count - 1), + ) + root.right = build( + (pre[0] + left_count + 1, pre[1]), + (ino[0] + left_count + 1, ino[1]), + ) + + return root + + return build((0, len(preorder) - 1), (0, len(inorder) - 1)) diff --git a/longest-palindromic-substring/alphaorderly.py b/longest-palindromic-substring/alphaorderly.py new file mode 100644 index 0000000000..63620d5d20 --- /dev/null +++ b/longest-palindromic-substring/alphaorderly.py @@ -0,0 +1,28 @@ +""" +시간복잡도: O(n^2) +공간복잡도: O(1) + +각 인덱스를 중심으로 팰린드롬을 확장하며 탐색 +확장 함수로 팰린드롬의 좌우 경계 반환 +가장 긴 팰린드롬 구간을 기록 +""" +class Solution: + def longestPalindrome(self, s: str) -> str: + best_left = best_right = 0 + + def expand(left: int, right: int) -> tuple[int, int]: + while left >= 0 and right < len(s) and s[left] == s[right]: + left -= 1 + right += 1 + + return left + 1, right - 1 + + for center in range(len(s)): + for left, right in ( + expand(center, center), + expand(center, center + 1), + ): + if right - left > best_right - best_left: + best_left, best_right = left, right + + return s[best_left : best_right + 1] diff --git a/rotate-image/alphaorderly.py b/rotate-image/alphaorderly.py new file mode 100644 index 0000000000..13a4df8a79 --- /dev/null +++ b/rotate-image/alphaorderly.py @@ -0,0 +1,18 @@ +""" +시간복잡도: O(n^2) +공간복잡도: O(1) + +- 행렬을 전치 행렬로 변환 +- 각 행을 중간 열을 기준으로 뒤집음 +""" +class Solution: + def rotate(self, matrix: List[List[int]]) -> None: + N = len(matrix) + + for r in range(N): + for c in range(r + 1, N): + matrix[r][c], matrix[c][r] = matrix[c][r], matrix[r][c] + + for r in range(N): + for c in range(N // 2): + matrix[r][c], matrix[r][-c - 1] = matrix[r][-c - 1], matrix[r][c] diff --git a/subtree-of-another-tree/alphaorderly.py b/subtree-of-another-tree/alphaorderly.py new file mode 100644 index 0000000000..a9e2572900 --- /dev/null +++ b/subtree-of-another-tree/alphaorderly.py @@ -0,0 +1,37 @@ +""" +시간복잡도: O(n + m) +공간복잡도: O(n + m) + +- subRoot 트리를 후위 순회하여 배열로 변환 +- root의 각 노드마다 후위 순회 결과 배열을 구함 +- 이 배열이 subRoot의 후위 순회 배열과 일치하는지 확인 +- 같으면 True 반환, 아니면 계속 탐색 +""" +class Solution: + def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool: + def postorder(node: TreeNode): + if not node: + return ["*"] + + left = postorder(node.left) + right = postorder(node.right) + return left + right + [node.val] + + sub_pos = postorder(subRoot) + + def check(node: TreeNode): + if not node: + return (["*"], False) + + left, left_valid = check(node.left) + right, right_valid = check(node.right) + + if left_valid or right_valid: + return ([], True) + + if left + right + [node.val] == sub_pos: + return ([], True) + + return (left + right + [node.val], False) + + return check(root)[1]