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
103 changes: 103 additions & 0 deletions Python3/102. Binary Tree Level Order Traversal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
## Step 1. Initial Solution

- BFSで探せば良いが、同じ深さをまとめて処理する方が分かりやすそう
- while文の中を同じ深さの処理にする
- 二重ループになるが、処理は分かりやすい

```python
# 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 levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if root is None:
return []
current_level_nodes: List[TreeNode] = [root]
result: List[List[int]] = []
while current_level_nodes:
node_value_list: List[int] = []

Choose a reason for hiding this comment

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

あまり list というデータ構造であることを示す特別な意味がない (vs. stack / queue) ように思うので、単に複数形で node_values などのほうがよいかなと思いました。next_level_nodes とも align しているので。

Choose a reason for hiding this comment

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

ちなみにPython3.9からは小文字の方が推奨になったようです。個人的には、LeetCodeの関数定義はそのままtyping.Listを残して、残りの部分は新しいバージョンに準拠するようにしています。

https://docs.python.org/3/library/typing.html#typing.List

next_level_nodes: List[TreeNode] = []
for node in current_level_nodes:
node_value_list.append(node.val)

Choose a reason for hiding this comment

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

リスト内包表記でresultに直接ノードの値のリストを入れることもできます。

if node.left is not None:
next_level_nodes.append(node.left)
if node.right is not None:
next_level_nodes.append(node.right)
result.append(node_value_list)
current_level_nodes = next_level_nodes
return result
```

### Complexity Analysis

- 時間計算量:O(n)
- 空間計算量:O(n)

## Step 2. Alternatives

- 深さを保持しておく方法

```python
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if root is None:
return []
nodes_and_depths: deque[Tuple[TreeNode, int]] = deque([(root, 0)])

Choose a reason for hiding this comment

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

細かいですが、関数名がlevelなのでdepthつかわずにlevelでもいいかもです

result: List[List[int]] = [[]]
while nodes_and_depths:
node, depth = nodes_and_depths.popleft()
if node.left is not None:
nodes_and_depths.append((node.left, depth + 1))
if node.right is not None:
nodes_and_depths.append((node.right, depth + 1))
if len(result) <= depth:
result.append([])
result[depth].append(node.val)
return result
```

- 変数名はもう少し考えても良いのかも
- resultの代わりにlevel_ordered_values
- https://github.com/Fuminiton/LeetCode/pull/26/files#diff-c9ad89199edb3e781ec524a54d5b5bd52c8baabcc71b3db537a3d4aeda37c9e6R58
- 下の書き方も変数が減って楽になる
- 個人的には悪くないと思った
Copy link

Choose a reason for hiding this comment

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

個人的にはあまり好きではないですが、長さが短ければこれでもいいのかもしれません。

https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.ho7q4rvwsa1g

- https://github.com/sakupan102/arai60-practice/pull/27/files#r1597353212

```python
while nodes:
level_ordered_values.append([])
for node in nodes:
level_ordered_values[-1].append(node.val)
```

- フィルター関数を用いるのもあり
- `nodes = list(filter(None, nodes))`
Copy link

Choose a reason for hiding this comment

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

この書き方がソフトウェアエンジニアの常識に含まれているか、あまり自信がありませんでした。

X (Twitter) でアンケート取ってみます。
https://x.com/nodchip/status/1932263139762229448

Copy link

Choose a reason for hiding this comment

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

別に Python を普段書いていないならば知らなくて当然と思いますが、
Python の Built-in function なので書くことは構わないでしょうし、別に一般論としてプロダクションのコードに未知のライブラリーが使われていることも普通ではないでしょうか。
https://source.chromium.org/search?q=%5Cbfilter%5C(%20filepath:.*%5C.py$%20-%22def%20filter%22%20case:yes&sq=

Copy link

Choose a reason for hiding this comment

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

念のため補足すると、 filter() 関数の第一引数に None を渡した場合、第二引数に渡された値から None を取り除いた値を返す、という挙動が、平均的なソフトウェアエンジニアにとって既知のものであるか、読みやすいかという点を気にしていました。問題なさそうということであれば、大丈夫だと思います。

- https://github.com/Fuminiton/LeetCode/pull/26/files#diff-c9ad89199edb3e781ec524a54d5b5bd52c8baabcc71b3db537a3d4aeda37c9e6R64
- if文で改行しないのはたまにやりたくなるが読んでいると一瞬びっくりする
- https://github.com/TORUS0818/leetcode/pull/28/files#diff-8c44f4aded8e694c335a78471f1f2b470fbd42a28276462da15a2f4ba70d3810R199

## Step 3. Final Solution

- あまり馴染みがなかったのでfilterを使って実装
- ちょっと変数名が長すぎて読みにくい印象になってしまった

```python
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if root is None:
return []
level_ordered_values: List[List[int]] = []
nodes_to_order: Deque[TreeNode] = deque([root])
while nodes_to_order:
level_ordered_values.append([])
for _ in range(len(nodes_to_order)):
node = nodes_to_order.popleft()
level_ordered_values[-1].append(node.val)
nodes_to_order.append(node.left)
nodes_to_order.append(node.right)
nodes_to_order = deque(filter(None, nodes_to_order))
return level_ordered_values

Choose a reason for hiding this comment

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

読みやすかったです。
特に問題ないと思います。

```