Skip to content

Commit 2cbac12

Browse files
committed
solve(w15): 105. Construct Binary Tree from Preorder and Inorder Traversal
1 parent 96231bd commit 2cbac12

File tree

1 file changed

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

1 file changed

+115
-0
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
2+
3+
from typing import List, Optional
4+
5+
# Definition for a binary tree node.
6+
class TreeNode:
7+
def __init__(self, val=0, left=None, right=None):
8+
self.val = val
9+
self.left = left
10+
self.right = right
11+
12+
"""
13+
트리를 유일하게 복원하기 위해서는 inorder가 필요하다. inorder는 left/right child를 구분할 수 있기 때문이다.
14+
- preorder + inorder: root를 먼저 알고, inorder로 구조 구분
15+
- postorder + postorder: root를 나중에 알고, inorder로 구조 구분
16+
preorder + postorder라면, 구조가 다른 트리를 구분할 수 없는 경우가 있다.
17+
"""
18+
19+
class Solution:
20+
def buildTree1(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
21+
"""
22+
[Complexity]
23+
- TC: O(n^2)
24+
- SC: O(n^2)
25+
(* pop(0), index(), slicing이 모두 O(n))
26+
27+
[Approach]
28+
preorder의 경우, root - left - right 순서로 방문하므로, root를 제일 먼저 찾을 수 있다.
29+
inorder의 경우, left - root - right 순서로 방문하므로,
30+
preorder의 매 단계에서 찾은 root에 대해 left와 right subtree를 다음과 같이 정의할 수 있다. (root 제외)
31+
- left subtree = inorder[:root_idx]
32+
- right subtree = inorder[root_idx + 1:]
33+
"""
34+
root = None
35+
36+
if inorder:
37+
# preorder root
38+
root_idx = inorder.index(preorder.pop(0))
39+
root = TreeNode(val=inorder[root_idx])
40+
# preorder left
41+
root.left = self.buildTree(preorder, inorder[:root_idx])
42+
# preorder right
43+
root.right = self.buildTree(preorder, inorder[root_idx + 1:])
44+
45+
return root
46+
47+
def buildTree2(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
48+
"""
49+
[Complexity]
50+
- TC: O(n^2)
51+
- SC: O(n^2)
52+
(* index(), slicing이 모두 O(n))
53+
54+
[Approach]
55+
이전 코드에서 pop(0)에 드는 O(n)을 줄이기 위해,
56+
preorder.reverse() 후 pop()으로 변경할 수 있다.
57+
"""
58+
preorder.reverse()
59+
60+
def solve(preorder, inorder):
61+
if inorder:
62+
# preorder root
63+
root_idx = inorder.index(preorder.pop())
64+
root = TreeNode(inorder[root_idx])
65+
# preorder left
66+
root.left = solve(preorder, inorder[:root_idx])
67+
# preorder right
68+
root.right = solve(preorder, inorder[root_idx + 1:])
69+
70+
return root
71+
72+
return solve(preorder, inorder)
73+
74+
def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
75+
"""
76+
[Complexity]
77+
- TC: O(n)
78+
- SC: O(n) (call stack)
79+
80+
[Approach]
81+
병목이었던 index()와 slicing을 O(1)로 최적화 할 수 있다.
82+
- index(): inorder의 index를 dict로 캐싱
83+
- slicing: start/end index로 추적
84+
이와 더불어 preorder의 root를 가리키는 index인 pre_idx를 사용하면 pop(0) 또는 pop()을 대체할 수도 있다.
85+
"""
86+
87+
# instead of index()
88+
inorder_idx = {num: i for i, num in enumerate(inorder)}
89+
90+
# preorder root (instead of pop())
91+
pre_idx = 0
92+
93+
# instead of inorder[in_left:in_right] slicing
94+
def solve(in_left, in_right):
95+
nonlocal pre_idx
96+
97+
# base condition
98+
if in_left >= in_right:
99+
return
100+
101+
# preorder root
102+
root_val = preorder[pre_idx]
103+
root = TreeNode(root_val)
104+
105+
# update indices
106+
root_idx = inorder_idx[root_val]
107+
pre_idx += 1
108+
109+
# recur
110+
root.left = solve(in_left, root_idx)
111+
root.right = solve(root_idx + 1, in_right)
112+
113+
return root
114+
115+
return solve(0, len(inorder))

0 commit comments

Comments
 (0)