diff --git a/Python3/105. Construct Binary Tree from Preorder and Inorder Traversal.md b/Python3/105. Construct Binary Tree from Preorder and Inorder Traversal.md new file mode 100644 index 0000000..5a673c5 --- /dev/null +++ b/Python3/105. Construct Binary Tree from Preorder and Inorder Traversal.md @@ -0,0 +1,153 @@ +## Step 1. Initial Solution + +- あまり上手く出来る方法が思いつかない + - どちらから処理したら良いのか + - そもそもpreorderとinorderがどんなものかよく知らなかった + - 上から作っていくならpreorderの先頭が大事? +- 再帰でやるのが分かりやすそう + - 取りあえず左と右に分けて再帰に任せる方針で実装 + - preorderの先頭が出るまでinorderで探して、出てきたら左と右で区切る + - 左は都度追加していく方が効率良さそうだが少し対称性が難あり + - left_inorderのままleft_preorderのフィルタリングをするとTLEになる + - setにして行うと通った + +```python +class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: + if not inorder: + return None + node = TreeNode(preorder[0]) + node_index = inorder.index(preorder[0]) + left_inorder = inorder[:node_index] + right_inorder = inorder[node_index+1:] + left_inorder_set = set(left_inorder) + left_preorder = [val for val in preorder if val in left_inorder_set] + node.left = self.buildTree(left_preorder, left_inorder) + right_inorder_set = set(right_inorder) + right_preorder = [val for val in preorder if val in right_inorder_set] + node.right = self.buildTree(right_preorder, right_inorder) + return node +``` + +### Complexity Analysis + +- 時間計算量:O(n^2) + - 再帰1回につきリスト長に比例する計算量で、再帰1回ごとに長さは1ずつ減っていくのでO(n*(n+1)/2)でO(n^2) +- 空間計算量:O(n^2) + - 上記と同じ理由 + +## Step 2. Alternatives + +- preorderをqueueにして処理していけばsetを作ったり探索する必要がなくなるとのこと + - O(n^2)ではあるが、かなり処理が減った + - かなり時間をかければ行けそうだがすぐには思いつかなかった + + ```python + class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: + preorder_queue = deque(preorder) + + def buildTreeHelper(queue: Deque[int], inorder: List[int]) -> Optional[TreeNode]: + if not inorder: + return None + node = TreeNode(queue.popleft()) + node_index = inorder.index(node.val) + node.left = buildTreeHelper(queue, inorder[:node_index]) + node.right = buildTreeHelper(queue, inorder[node_index+1:]) + return node + + return buildTreeHelper(preorder_queue, inorder) + ``` + +- 各再帰の実行を定数オーダーにする方法があるとのこと + - これで全体としてはO(n)で書ける + - スライスせずに位置だけ保持するようにする + - indexを予めsetに入れておく + + ```python + class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: + num_nodes = len(inorder) + inorder_val_to_index = {inorder[i]: i for i in range(num_nodes)} + + def buildTreeHelper(preorder_index:int, start: int, end: int) -> Optional[TreeNode]: + if start == end: + return None + node = TreeNode(preorder[preorder_index]) + inorder_index = inorder_val_to_index[node.val] + node.left = buildTreeHelper(preorder_index + 1, start, inorder_index) + node.right = buildTreeHelper(preorder_index + (inorder_index - start) + 1, inorder_index + 1, end) + return node + + return buildTreeHelper(0, 0, num_nodes) + ``` + + - 後から調べたら同じような解法が多そう + - https://github.com/ryoooooory/LeetCode/pull/32/files#diff-68e75a2f6f81f4cbca273ad08e9da37d22af2faf3b0629ca713220b3ca892b92R11 + - preorder_indexの管理方法について + - 一つ上の方法のようにqueueとして処理する方法 + - 関数の引数に入れる方法 + - preorder_indexをクラス変数で保持する方法 + - nonlocalを使う方法 + + あたりが考えられるが、安全なのは上二つか + +- iterativeでも解いてみる + - ようやく状況が理解できてきたような感覚 + + ```python + class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: + num_nodes = len(inorder) + inorder_val_to_index = {inorder[i]: i for i in range(num_nodes)} + preorder_index = 0 + root = TreeNode() + nodes_and_ranges = [(root, 0, num_nodes)] + + while preorder_index != num_nodes: + node, start, end = nodes_and_ranges.pop() + node.val = preorder[preorder_index] + preorder_index += 1 + inorder_index = inorder_val_to_index[node.val] + if inorder_index + 1 < end: + node.right = TreeNode() + nodes_and_ranges.append((node.right, inorder_index + 1, end)) + if start < inorder_index: + node.left = TreeNode() + nodes_and_ranges.append((node.left, start, inorder_index)) + return root + ``` + +- inorder順に作っていく方法もあるのかなと思っていたらこの辺があった + - 時間がある時に確認したい + - https://github.com/nittoco/leetcode/commit/709d0e3d75d71e72ef48b532f5334ad43e05fe87#diff-8de91d4e0a2e151bab0747855e5a727ccc43f01a11c6c9c1c8cc75b604db4edeR245-R331 + +## Step 3. Final Solution + +- 再帰で3回、iterativeで1回 + - stackの名前が長すぎるのが少し気になる + +```python +class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: + num_nodes: int = len(preorder) + inorder_val_to_index: Dict[int, int] = {inorder[i]: i for i in range(num_nodes)} + root: TreeNode = TreeNode() + nodes_preorder_indices_and_ranges: List[Tuple[TreeNode, int, int, int]] = \ + [(root, 0, 0, num_nodes)] + + while nodes_preorder_indices_and_ranges: + node, preorder_index, start, end = nodes_preorder_indices_and_ranges.pop() + node.val = preorder[preorder_index] + inorder_index = inorder_val_to_index[node.val] + if inorder_index + 1 < end: + node.right = TreeNode() + nodes_preorder_indices_and_ranges.append((node.right, \ + preorder_index + inorder_index - start + 1, inorder_index + 1, end)) + if start < inorder_index: + node.left = TreeNode() + nodes_preorder_indices_and_ranges.append((node.left, \ + preorder_index + 1, start, inorder_index)) + + return root +```