Skip to content

Create taurus09318976.py #1668

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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

90 changes: 90 additions & 0 deletions subtree-of-another-tree/taurus09318976.py
Original file line number Diff line number Diff line change
@@ -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))