Skip to content

Commit 838bd0f

Browse files
committed
add solution: construct-binary-tree-from-preorder-and-inorder-traversal
1 parent dbb43fc commit 838bd0f

File tree

1 file changed

+95
-0
lines changed
  • construct-binary-tree-from-preorder-and-inorder-traversal

1 file changed

+95
-0
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Definition for a binary tree node.
2+
# class TreeNode:
3+
# def __init__(self, val=0, left=None, right=None):
4+
# self.val = val
5+
# self.left = left
6+
# self.right = right
7+
class Solution:
8+
'''
9+
A. 재귀 풀이
10+
preorder와 inorder의 각각의 범위를 조정하여 트리를 생성
11+
'''
12+
def buildTreeA(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
13+
def setTree(pre_left, pre_right, in_left, in_right):
14+
# 재귀 종료 조건: preorder 범위가 유효하지 않은 경우
15+
if pre_left > pre_right:
16+
return None
17+
18+
val = preorder[pre_left] # preorder의 현재 루트 노드 값 가져오기
19+
mid = TreeNode(val) # 루트 노드를 먼저 생성
20+
21+
mid_inorder = inorder_idx_map[val] # 루트 노드의 inorder 인덱스 가져오기
22+
left_size = mid_inorder - in_left # 왼쪽 서브트리의 크기 계산
23+
24+
# 왼쪽 서브트리 생성: preorder와 inorder의 범위를 왼쪽 서브트리로 조정
25+
mid.left = setTree(
26+
pre_left + 1, pre_left + left_size, in_left, mid_inorder - 1
27+
)
28+
29+
# 오른쪽 서브트리 생성: preorder와 inorder의 범위를 오른쪽 서브트리로 조정
30+
mid.right = setTree(
31+
pre_left + left_size + 1, pre_right, mid_inorder + 1, in_right
32+
)
33+
34+
return mid # 현재 노드 반환
35+
36+
# inorder를 값 -> 인덱스 맵핑한 딕셔너리 생성 (O(n))
37+
inorder_idx_map = {value: idx for idx, value in enumerate(inorder)}
38+
39+
# 트리 생성 시작 (preorder와 inorder 전체 범위 사용)
40+
return setTree(0, len(preorder) - 1, 0, len(inorder) - 1)
41+
42+
43+
'''
44+
# B. 재귀 풀이 + 공간 최적화
45+
# 레퍼런스 링크의 풀이 2: https://www.algodale.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
46+
# 특징: 순회 시마다 인덱스를 찾는 과정이 있음
47+
'''
48+
def buildTreeB(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
49+
# pre: 현재 preorder에서 확인할 인덱스
50+
# start, end: inorder에서 사용할 시작/종료 범위
51+
def setTree(pre, start, end):
52+
# 재귀 종료 조건: 범위가 잘못되었거나 트리를 더 이상 만들 필요가 없는 경우
53+
if not (pre < len(preorder) and start <= end): # preorder에서 확인할 인덱스가 범위에서 나감, 투 포인터가 만남
54+
return None
55+
56+
val = preorder[pre] # 현재 노드의 값
57+
root = inorder.index(val) # 트리/서브트리의 루트 노드 인덱스 찾기 - SC: O(n)
58+
59+
left = setTree(pre + 1, start, root - 1)
60+
# inorder에서 root노드의 왼쪽은 왼쪽 서브트리
61+
# pre의 변화: 왼쪽 서브트리의 루트 노드를 찾기 위해 +1 이동
62+
63+
right = setTree(pre + 1 + root - start, root + 1, end)
64+
# inorder에서 root노드의 오른쪽은 오른쪽 서브트리
65+
# pre의 변화: 오른쪽 서브트리의 루트 노드를 찾기 위해 +1 이동 + (root - start) 👈 왼쪽 서브트리의 크기 만큼 더 이동
66+
67+
return TreeNode(preorder[pre], left, right) # 트리 노드 생성
68+
69+
# preorder 최초 인덱스 = 루트 노드(0), inorder의 처음(0)과 끝(len(inorder) - 1) 인덱스
70+
return setTree(0, 0, len(inorder) - 1)
71+
72+
'''
73+
C. 재귀 풀이 + 시간 최적화
74+
레퍼런스 링크의 풀이 3: https://www.algodale.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
75+
특징: A에서 preorder를 찾는 O(n) 과정을 해시 테이블을 사용하여 O(1)로 최적화
76+
'''
77+
def buildTreeC(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
78+
# enumerate: 인덱스와 값을 동시에 반환
79+
# inorder를 val -> idx로 매핑한 딕셔너리 생성
80+
inorder_index_map = {val: idx for idx, val in enumerate(inorder)}
81+
# preorder를 순회하기 위한 iterator 객체 생성
82+
pre_iter = iter(preorder)
83+
84+
def setTree(start, end):
85+
if start > end: # 재귀 종료 조건: 범위가 잘못되었거나 트리를 더 이상 만들 필요가 없는 경우
86+
return None
87+
88+
root_val = next(pre_iter) # 현재 노드의 값, 매 순회마다 다음 preorder 노드(root)의 값을 가져옴
89+
root = inorder_index_map[root_val] # 트리/서브트리의 루트 노드 인덱스를 O(1) 시간으로 찾기
90+
91+
left = setTree(start, root - 1) # 왼쪽 서브트리
92+
right = setTree(root + 1, end) # 오른쪽 서브트리
93+
return TreeNode(root_val, left, right) # 트리 노드 생성
94+
95+
return setTree(0, len(inorder) - 1) # inorder의 처음(0)과 끝(len(inorder) - 1) 인덱스

0 commit comments

Comments
 (0)