From c348167045f9d6e820d4fdf10635bae8a484f52f Mon Sep 17 00:00:00 2001 From: tom4649 Date: Wed, 25 Mar 2026 09:48:58 +0900 Subject: [PATCH 1/2] Add first step --- 105/memo.md | 13 +++++++++++++ 105/sol1.py | 31 +++++++++++++++++++++++++++++++ 105/sol2.py | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 105/memo.md create mode 100644 105/sol1.py create mode 100644 105/sol2.py diff --git a/105/memo.md b/105/memo.md new file mode 100644 index 0000000..4f1c34f --- /dev/null +++ b/105/memo.md @@ -0,0 +1,13 @@ + +# 105. Construct Binary Tree from Preorder and Inorder Traversal + +- sol1: 愚直な方針。再帰でpreorderの先頭が根であり、preorderでその位置を特定して再帰。 + - 時間計算量: 根の探索とスライス作成で最悪O(n**2) + - 空間計算量: スライス作成で最悪O(n**2) + - 再帰スタック:O(h), 最悪O(n) +- sol2: 再帰は変えずに、inorder の値→index の辞書を作って (O(1)) で位置を引き、再帰は「配列の範囲(左右境界)」で表す(スライスしない)。 + - 時間計算量: 最初の val_to_indexの計算でO(n) + - 空間計算量: val_to_indexのみなのでO(n) + - 再帰スタックは上と同じ + - 結構速くなったので、これが想定解か +- TODO: 他の人の解答をみるなど 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..049ca5e --- /dev/null +++ b/105/sol2.py @@ -0,0 +1,41 @@ +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)) From e34bb35fee19658e146efbb7ce455efc239c2926 Mon Sep 17 00:00:00 2001 From: tom4649 Date: Thu, 26 Mar 2026 06:14:02 +0900 Subject: [PATCH 2/2] 105. Construct Binary Tree from Preorder and Inorder Traversal --- 105/memo.md | 21 +++++++++++++++++++-- 105/sol2.py | 5 ++++- 105/sol3.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 105/sol3.py diff --git a/105/memo.md b/105/memo.md index 4f1c34f..33b67cb 100644 --- a/105/memo.md +++ b/105/memo.md @@ -5,9 +5,26 @@ - 時間計算量: 根の探索とスライス作成で最悪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) - 再帰スタックは上と同じ - - 結構速くなったので、これが想定解か -- TODO: 他の人の解答をみるなど + - 結構速くなった + - 時間の具体的な見積もり: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/sol2.py b/105/sol2.py index 049ca5e..9ac43ac 100644 --- a/105/sol2.py +++ b/105/sol2.py @@ -5,6 +5,7 @@ try: TreeNode # type: ignore[name-defined] except NameError: + class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val @@ -21,7 +22,9 @@ def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNod 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]: + 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]) 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))