diff --git a/105/memo.md b/105/memo.md new file mode 100644 index 0000000..33b67cb --- /dev/null +++ b/105/memo.md @@ -0,0 +1,30 @@ + +# 105. Construct Binary Tree from Preorder and Inorder Traversal + +- sol1: 愚直な方針。再帰でpreorderの先頭が根であり、preorderでその位置を特定して再帰。 + - 時間計算量: 根の探索とスライス作成で最悪O(n**2) + - 空間計算量: スライス作成で最悪O(n**2) + - 再帰スタック:O(h), 最悪O(n) + - 時間の具体的な見積もり:n = 3000, Python 10^7として  3000 **2 / 10^7 = 0.9s +- sol2: 再帰は変えずに、inorder の値→index の辞書を作って (O(1)) で位置を引き、再帰は「配列の範囲(左右境界)」で表す(スライスしない)。 + - 時間計算量: 最初の val_to_indexの計算でO(n) + - 空間計算量: val_to_indexのみなのでO(n) + - 再帰スタックは上と同じ + - 結構速くなった + - 時間の具体的な見積もり:n = 3000, Python 10^7として  3000 / 10^7 = 3 * 10**-4 + +https://github.com/mamo3gr/arai60/blob/105_construct-binary-tree-from-preorder-and-inorder-traversal/105_construct-binary-tree-from-preorder-and-inorder-traversal/step2.py + +- 区間を表すclass Spanを作っている +- 無駄がないといういみでは自分の左端とnum_childrenを持つ形でも良さそう +- frontierを使った再帰ループでも書いている + +コメント集 +https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.1rv0z8fm6lc3 + +- inorderの順番で構築: https://discord.com/channels/1084280443945353267/1247673286503039020/1300957719074967603 + - C++をPythonに変換した (sol3) + - inorderで自分より前(自分の左)、preorder自分より後(=自分の子供)のノードをleftでまとめて回収 + - 自分の左は確定しているが、右が確定していないノードをstackに積む。これらはinorderで探索しているので、親が先にstackに入る(後に出る)。よって、これらを右に重ねていけばよい。 + - 自分で書くのは無理 + diff --git a/105/sol1.py b/105/sol1.py new file mode 100644 index 0000000..4edc96e --- /dev/null +++ b/105/sol1.py @@ -0,0 +1,31 @@ +from typing import List, Optional + + +# 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: List[int], inorder: List[int]) -> Optional[TreeNode]: + assert len(preorder) == len(inorder), f"{len(inorder)}, {len(preorder)}" + if len(preorder) == 0: + return None + root = TreeNode(preorder[0]) + if len(preorder) == 1: + return root + idx_inorder_root = -1 + for idx_inorder in range(len(inorder)): + if inorder[idx_inorder] == root.val: + idx_inorder_root = idx_inorder + break + left_children_inorder = inorder[:idx_inorder_root] + left_children_preorder = preorder[1 : idx_inorder_root + 1] + right_children_inorder = inorder[idx_inorder_root + 1 :] + right_children_preorder = preorder[idx_inorder_root + 1 :] + root.left = self.buildTree(left_children_preorder, left_children_inorder) + root.right = self.buildTree(right_children_preorder, right_children_inorder) + return root diff --git a/105/sol2.py b/105/sol2.py new file mode 100644 index 0000000..9ac43ac --- /dev/null +++ b/105/sol2.py @@ -0,0 +1,44 @@ +from typing import List, Optional + + +# Definition for a binary tree node. +try: + TreeNode # type: ignore[name-defined] +except NameError: + + 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: List[int], inorder: List[int]) -> Optional[TreeNode]: + assert len(preorder) == len(inorder), f"{len(inorder)}, {len(preorder)}" + if not preorder: + return None + inorder_val_to_idx = {} + for i, val in enumerate(inorder): + inorder_val_to_idx[val] = i + + def buildTree_w_index( + pre_left: int, in_left: int, num_children: int + ) -> Optional[TreeNode]: + if num_children <= 0: + return None + root = TreeNode(preorder[pre_left]) + if num_children == 1: + return root + idx_root_inorder = inorder_val_to_idx[root.val] + num_left_children = idx_root_inorder - in_left + num_right_children = num_children - num_left_children - 1 + root.left = buildTree_w_index(pre_left + 1, in_left, num_left_children) + root.right = buildTree_w_index( + pre_left + 1 + num_left_children, + idx_root_inorder + 1, + num_right_children, + ) + return root + + return buildTree_w_index(0, 0, len(preorder)) diff --git a/105/sol3.py b/105/sol3.py new file mode 100644 index 0000000..a0e7fd2 --- /dev/null +++ b/105/sol3.py @@ -0,0 +1,43 @@ +from typing import Dict, List, Optional + + +# class TreeNode: +# def __init__( +# self, +# val: int = 0, +# left: Optional["TreeNode"] = None, +# right: Optional["TreeNode"] = None, +# ): +# self.val = val +# self.left = left +# self.right = right + + +class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: + if not preorder: + return None + + preorder_position = {val: i for i, val in enumerate(preorder)} + + # contains all nodes whose .right hasn't been decided yet. + stack = [] + + def gather_descendants(node_position): + child = None + while stack: + back = stack[-1] + if preorder_position[back.val] < node_position: + break + stack.pop() + back.right = child + child = back + return child + + for val in inorder: + node = TreeNode(val) + node_position = preorder_position[node.val] + node.left = gather_descendants(node_position) + stack.append(node) + + return gather_descendants(-(10**30))