Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
## Step 1. Initial Solution

- あまり上手く出来る方法が思いつかない
- どちらから処理したら良いのか
- そもそもpreorderとinorderがどんなものかよく知らなかった

Choose a reason for hiding this comment

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

今回の問題に関係はないですが、postorderも一緒に覚えて方がいいと思います。

- 上から作っていくならpreorderの先頭が大事?
- 再帰でやるのが分かりやすそう
- 取りあえず左と右に分けて再帰に任せる方針で実装
- preorderの先頭が出るまでinorderで探して、出てきたら左と右で区切る
- 左は都度追加していく方が効率良さそうだが少し対称性が難あり
- left_inorderのままleft_preorderのフィルタリングをするとTLEになる
Copy link

Choose a reason for hiding this comment

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

念のため確認させてください。この時の時間計算量と、おおよその処理時間はどれくらいになりますか?

- 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:]
Copy link

Choose a reason for hiding this comment

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

Step1のコードに細かいことを言って恐縮なんですが、自分ならright_inorderはrightの話なので後(L26)に持ってきますね

left_inorder_set = set(left_inorder)
left_preorder = [val for val in preorder if val in left_inorder_set]
Copy link

Choose a reason for hiding this comment

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

あと、やや冗長なのでleft_preorder = [val for val in preorder if val in set(left_inorder)] とすると思います

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を作ったり探索する必要がなくなるとのこと
Copy link

Choose a reason for hiding this comment

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

この解法、queueの処理される順番が追いにくく個人的にはあまり好きではないです

- O(n^2)ではあるが、かなり処理が減った
- かなり時間をかければ行けそうだがすぐには思いつかなかった

```python
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
preorder_queue = deque(preorder)

Choose a reason for hiding this comment

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

preorder の添字を0から1づつ増やしていくことでも実現できますね。


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]:
Copy link

Choose a reason for hiding this comment

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

: のあとにスペースを空けることをお勧めします。

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を使う方法

あたりが考えられるが、安全なのは上二つか

Choose a reason for hiding this comment

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

queueとして処理する方法 と nonlocalを使う方法は実質同じかなと思ったのですが、安全かに差があると思われた理由を教えていただきたいです。nonlocal と書いてある方が書き換えていることが明確であるという気もします。

Copy link

Choose a reason for hiding this comment

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

複数回呼べるか、スレッド並列か、などの話かと思います。
nonlocal でも二重の関数にしてあげればこの問題はないですね。

Choose a reason for hiding this comment

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

その観点まで考えられていませんでした。コメントありがとうございます。


- 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:

Choose a reason for hiding this comment

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

最初right側から書いていることを不思議に思いましたが、leftの方をstackで末尾に入れないといけないからということですね。コメントがあるとスムーズに読めると思いました。

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の名前が長すぎるのが少し気になる
Copy link

Choose a reason for hiding this comment

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

https://docs.python.org/3/library/dataclasses.html
dataclass使うと、中身の定義をdataclassの方に書けるので簡潔になりますね


```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, \

Choose a reason for hiding this comment

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

Do not use a backslash for explicit line continuation.
というのもあるようです。
https://google.github.io/styleguide/pyguide.html#32-line-length

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
```