diff --git a/construct-binary-tree-from-preorder-and-inorder-traversal/taurus09318976.py b/construct-binary-tree-from-preorder-and-inorder-traversal/taurus09318976.py new file mode 100644 index 000000000..597237d0e --- /dev/null +++ b/construct-binary-tree-from-preorder-and-inorder-traversal/taurus09318976.py @@ -0,0 +1,93 @@ +""" +문제의도: 이 문제는 트리 순회의 특성을 이해하고 재귀적 분할정복을 연습하는 문제임. +-> preorder로 루트 찾고, inorder로 좌우 나누고, 재귀로 반복 +- 전위 순회(preorder): 루트 -> 왼쪽 -> 오른쪽 +- 중위 순회(inorder): 왼쪽 -> 루트 -> 오른쪽 + +해결 방법: +1. preorder의 첫 번째 원소가 현재 트리의 루트임을 이용 +2. inorder에서 루트의 위치를 찾아 왼쪽/오른쪽 서브트리 분할 +3. preorder에서도 대응하는 부분을 분할 +4. 왼쪽/오른쪽 서브트리에 대해 재귀적으로 같은 과정 반복 +-> - 루트를 알면 inorder에서 왼쪽/오른쪽을 구분할 수 있음! + +시간복잡도: O(n²) +- index() 메서드: 배열에서 값을 찾기 위해 최악의 경우 전체 배열을 순회 → O(n) +- 재귀 호출 횟수: 모든 노드에 대해 한 번씩 호출 → n번 +- 전체 시간복잡도: n번의 호출 × 각 호출마다 O(n) = O(n²) + +공간복잡도: O(n) +- 재귀 호출 스택의 깊이: O(n) (편향 트리일 때) +- 새로 생성하는 배열들: O(n) + +예시 설명 : +예시1의 경우 + +1단계: preorder의 첫 번째는 항상 루트 +[3, 9, 20, 15, 7] → 3이 루트 + +2단계: inorder에서 루트 위치로 좌우 분할 +[9, 3, 15, 20, 7] → 3 기준으로 [9] | [15, 20, 7] +- 왼쪽: [9] (인덱스 0까지) +- 오른쪽: [15, 20, 7] (인덱스 2부터) + +3단계: 왼쪽 서브트리 크기로 preorder 분할 +왼쪽 크기가 1이면 preorder에서도 1개만 가져옴 +- 루트 제외: [9, 20, 15, 7] +- 왼쪽 크기(1)만큼: [9] +- 나머지: [20, 15, 7] + +4단계: 재귀적으로 같은 과정 반복 +- 왼쪽: buildTree([9], [9]) → 노드 9 +- 오른쪽: buildTree([20, 15, 7], [15, 20, 7]) → 서브트리 + +최종 결과: + 3 + / \ + 9 20 + / \ + 15 7 + + +주의사항: +- 배열 슬라이싱 범위 조심하기 +- 빈 배열일 때 None 반환하기 +- inorder에서 루트 위치 정확히 찾기 + +""" + +# 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 + + +class Solution: + def buildTree(self, preorder, inorder): + # 1. 기본 케이스: 빈 배열이면 None 반환 + if not preorder or not inorder: + return None + + # 2. preorder의 첫 번째가 루트 + root_val = preorder[0] + root = TreeNode(root_val) + + # 3. inorder에서 루트 위치 찾기 + root_index = inorder.index(root_val) + + # 4. inorder를 루트 기준으로 분할 + left_inorder = inorder[:root_index] + right_inorder = inorder[root_index + 1:] + + # 5. preorder도 해당 크기만큼 분할 + left_preorder = preorder[1:1 + len(left_inorder)] + right_preorder = preorder[1 + len(left_inorder):] + + # 6. 재귀적으로 왼쪽과 오른쪽 서브트리 구성 + root.left = self.buildTree(left_preorder, left_inorder) + root.right = self.buildTree(right_preorder, right_inorder) + + return root + diff --git a/subtree-of-another-tree/taurus09318976.py b/subtree-of-another-tree/taurus09318976.py new file mode 100644 index 000000000..0ff609f0e --- /dev/null +++ b/subtree-of-another-tree/taurus09318976.py @@ -0,0 +1,90 @@ +""" +문제의 본질: "큰 트리 안에 작은 트리와 똑같은 부분이 있나?" + +해결 아이디어: +1. 큰 트리의 모든 위치를 하나씩 확인해보기 +2. 각 위치에서 "여기서 시작하는 서브트리가 찾는 트리와 완전히 같은가?" 확인하기 + +해결 방법: +1. 큰 트리의 모든 노드를 순회 +2. 각 노드에서 시작하는 서브트리가 찾는 트리와 같은지 확인 +3. 같은 트리인지 확인하는 것은 별도 함수로 분리 + +헤결에 필요한 두 개의 함수: +1. isSubtree 함수 (메인 로직): "어디서 찾을 수 있나?" (탐색 담당) + - root가 None이면 False 반환 (빈 트리에선 찾을 수 없음) + - 현재 위치에서 완전히 같은 트리인지 확인 + - 같지 않다면 왼쪽, 오른쪽 자식에서 재귀적으로 찾기 + +2. isSameTree 함수 (보조 로직): "여기서 완전히 같나?" (비교 담당) + - 두 트리가 완전히 같은지 확인 + - 구조와 값이 모두 같아야 함 + - 재귀적으로 모든 노드 비교 + +왜 이렇게 풀까? +- 서브트리는 어느 노드에서든 시작할 수 있음 +- 따라서 모든 가능한 시작점을 확인해야 함 +- 각 시작점에서 완전히 같은 트리인지 확인하면 됨 + +기억하기 쉬운 방법: +1. "모든 노드에서 시도해보기" (isSubtree) +2. "완전히 같은지 확인하기" (isSameTree) +이 두 가지 기능을 분리해서 생각하기! + +시간복잡도: O(m × n) +- m: root 트리의 노드 개수 +- n: subRoot 트리의 노드 개수 +- 최악의 경우 root의 모든 노드에서 subRoot와 비교 + +공간복잡도: O(max(m, n)) +- 재귀 호출 스택의 깊이는 트리의 높이와 같음 +- 균형 트리라면 O(log n), 편향 트리라면 O(n) +""" + + +# 1. 먼저 TreeNode 클래스 정의 (이진 트리의 노드를 나타냄) +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val # 노드의 값 + self.left = left # 왼쪽 자식 노드 + self.right = right # 오른쪽 자식 노드 + + +class Solution: + # 2. 메인 함수: root 트리에서 subRoot와 같은 서브트리가 있는지 확인 + def isSubtree(self, root, subRoot): + + # 기본 케이스 1: 큰 트리가 비어있으면 서브트리를 찾을 수 없음 + if not root: + return False + + # 기본 케이스 2: 현재 노드에서 서브트리와 완전히 일치하는지 확인 + if self.isSameTree(root, subRoot): + return True + + # 재귀 케이스: 왼쪽 서브트리 또는 오른쪽 서브트리에서 찾기 + # 왼쪽 서브트리에서 찾거나 OR 오른쪽 서브트리에서 찾으면 True + return (self.isSubtree(root.left, subRoot) or + self.isSubtree(root.right, subRoot)) + + + # 3. 보조 함수: 두 트리가 완전히 같은지 확인 (구조와 값 모두) + def isSameTree(self, tree1, tree2): + + # 기본 케이스 1: 둘 다 비어있으면 같음 + if not tree1 and not tree2: + return True + + # 기본 케이스 2: 하나만 비어있으면 다름 + if not tree1 or not tree2: + return False + + # 기본 케이스 3: 현재 노드의 값이 다르면 다름 + if tree1.val != tree2.val: + return False + + # 재귀 케이스: 왼쪽 서브트리와 오른쪽 서브트리가 모두 같아야 함 + # 왼쪽끼리 같고 AND 오른쪽끼리 같아야 전체가 같음 + return (self.isSameTree(tree1.left, tree2.left) and + self.isSameTree(tree1.right, tree2.right)) +