Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions 105/memo.md
Original file line number Diff line number Diff line change
@@ -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に入る(後に出る)。よって、これらを右に重ねていけばよい。
- 自分で書くのは無理

31 changes: 31 additions & 0 deletions 105/sol1.py
Original file line number Diff line number Diff line change
@@ -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
44 changes: 44 additions & 0 deletions 105/sol2.py
Original file line number Diff line number Diff line change
@@ -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(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

メソッド名のwが何かを読み取れなかったので、略ではなく単語を使うことをお勧めします。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

withの略でしたが、わかりづらいのかもしれませんね。省略せずに書こうと思います。

pre_left: int, in_left: int, num_children: int
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

私はこの書き方を検討しきれていませんでした。確かに、子の数がわかっているのでこの値に名前をつけて引数にするのが、とてもわかりやすく感じました。

) -> 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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好みの範疇ですが、numよりはcountの方が個数ということを読み取りやすいと思いました。

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))
43 changes: 43 additions & 0 deletions 105/sol3.py
Original file line number Diff line number Diff line change
@@ -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))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この数字がどうやって決まっているのかわかりづらいので、Python にするのだったら float("-inf") とかの方が意図が伝わりやすいかもしれません。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ご指摘の通りですね、ありがとうございます