-
Notifications
You must be signed in to change notification settings - Fork 0
102.binary tree level order traversal #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import sys | ||
| import timeit | ||
|
|
||
|
|
||
| def main() -> None: | ||
| n = 200000 | ||
| number = 200 | ||
| repeat = 7 | ||
|
|
||
| list_data = list(range(n)) | ||
| tuple_data = tuple(list_data) | ||
|
|
||
| def loop_list() -> int: | ||
| s = 0 | ||
| for x in list_data: | ||
| s += x | ||
| return s | ||
|
|
||
| def loop_tuple() -> int: | ||
| s = 0 | ||
| for x in tuple_data: | ||
| s += x | ||
| return s | ||
|
|
||
| assert loop_list() == loop_tuple() | ||
|
|
||
| t_list = min(timeit.repeat(loop_list, number=number, repeat=repeat)) | ||
| t_tuple = min(timeit.repeat(loop_tuple, number=number, repeat=repeat)) | ||
|
|
||
| print(f"n={n} number={number} repeat={repeat} (best of repeat)") | ||
| print(f"list : {t_list:.6f} s") | ||
| print(f"tuple: {t_tuple:.6f} s") | ||
| print(f"tuple/list: {t_tuple / t_list:.4f}x") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| # 102. Binary Tree Level Order Traversal | ||
| [リンク](https://leetcode.com/problems/binary-tree-level-order-traversal/description/) | ||
|
|
||
| - sol1.py: 再帰でかいたがロジックがやや冗長かもしれない | ||
| - DFSで帰りがけ順にマージする | ||
| - 木が偏っている場合、時間計算量O(n**2) | ||
| - 空間計算量 O(n)+O(h) | ||
|
|
||
| - https://github.com/mamo3gr/arai60/blob/102_binary-tree-level-order-traversal/102_binary-tree-level-order-traversal/step2.py | ||
| - 行きがけ順の再帰 | ||
| - 似た感じで書く(sol2.py) | ||
| - 時間計算量O(n**2) | ||
| - 空間計算量 O(n)+O(h) | ||
|
|
||
| - https://discord.com/channels/1084280443945353267/1192728121644945439/1194203372115464272 | ||
| - if elseで例外処理を行うより、ifで例外だけを処理した方が良い | ||
| > 「機械の使い方の説明です。まず、青いランプが5つついていることを確認してください。ついている場合、…使い方の説明…。ランプがついていなかった場合は、直ちに使用を中止して事務所に連絡してください。…機械の使い方の続き…。」 | ||
|
|
||
| - queueを使った BFS (sol3.py) | ||
| - 時間計算量 O(n) | ||
| - 空間計算量 O(n) | ||
|
|
||
| - 再帰には深さに限界があるなど制約が多いので、ループに書き直せるようにしておく | ||
| - https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.deivkzaqvetb | ||
| - スタックをループに直すのは頻繁に必要になる。 | ||
| - 再帰だとログが難しい | ||
| - spl2_loop.py:再帰+dfs | ||
| > 例えばですけれども、1万回くらい呼ぶと1回くらいおかしな動きをする機能があって、乱数などが絡んでいるから再現も難しいので、ある特定の if 文を通ったときにログを出力したいとします。再帰で書いていると、そもそもどういう状況でそこに到達したのかの関数の呼び出し元の情報などを出力するのが大変です。 | ||
| - https://github.com/irohafternoon/LeetCode/pull/6#discussion_r2019026748 | ||
|
|
||
| - Java デフォルトが 1M のスタックサイズ、C は 10M くらいが普通。 | ||
| - Python setrecursionlimit: 言語処理系が設ける再帰上限 | ||
| - クラッシュ(Cスタック破壊)を避けるために、先に例外で止める | ||
| - https://docs.python.org/3/library/sys.html#sys.getrecursionlimit | ||
| - https://docs.python.org/3/library/sys.html#sys.setrecursionlimit | ||
| - 言語処理形が上限を設ける | ||
| - クイックソートで長い方を末尾再帰最適化するのもスタックオーバーフローを防ぐため | ||
| - https://nuc.hatenadiary.org/entry/2021/03/31#:~:text=%E7%9F%AD%E3%81%84%E6%96%B9%E3%81%8B%E3%82%89%E5%86%8D%E5%B8%B0%E3%81%97%E3%81%A6%E3%80%81%E9%95%B7%E3%81%84%E6%96%B9%E3%81%AF%E6%9C%AB%E5%B0%BE%E5%86%8D%E5%B8%B0%E6%9C%80%E9%81%A9%E5%8C%96%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8 | ||
|
|
||
| - tupleとlistのベンチマーク: tupleの方がややはやい | ||
| n=200000 number=200 repeat=7 (best of repeat) | ||
| list : 1.694295 s | ||
| tuple: 1.462984 s | ||
| tuple/list: 0.8635x | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| from itertools import zip_longest | ||
|
|
||
|
|
||
| # 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: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ロジックが把握しにくく感じました。自分に関数型言語の素養があまりないためかもしれません。 |
||
| def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: | ||
| if root is None: | ||
| return [] | ||
| left_level_order = self.levelOrder(root.left) | ||
| right_level_order = self.levelOrder(root.right) | ||
| merged_level_order = [[root.val]] | ||
| for left, right in zip_longest( | ||
| left_level_order, right_level_order, fillvalue=[] | ||
| ): | ||
| merged_level_order.append(left + right) | ||
| return merged_level_order | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # 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]]: | ||
| level_order = [] | ||
|
|
||
| def append_value_by_level(node, level): | ||
| if node is None: | ||
| return | ||
|
|
||
| if level == len(level_order): | ||
| level_order.append([]) | ||
| level_order[level].append(node.val) | ||
|
|
||
| append_value_by_level(node.left, level + 1) | ||
| append_value_by_level(node.right, level + 1) | ||
|
|
||
| append_value_by_level(root, 0) | ||
| return level_order |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| from collections import deque | ||
|
|
||
| # 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 [] | ||
| level_order = [] | ||
| node_to_traverse = deque([(0, root)]) | ||
|
|
||
| while node_to_traverse: | ||
| level, node = node_to_traverse.pop() | ||
| for child in (node.right, node.left): | ||
| if child is not None: | ||
| node_to_traverse.append((level + 1, child)) | ||
| while len(level_order) <= level: | ||
| level_order.append([]) | ||
| level_order[level].append(node.val) | ||
| return level_order |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| from collections import deque | ||
|
|
||
| # 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]]: | ||
| level_order = [] | ||
| if root is None: | ||
| return level_order | ||
| queue = deque([root]) | ||
| while queue: | ||
| values_by_level = [] | ||
| level_size = len(queue) | ||
| for _ in range(level_size): | ||
|
Comment on lines
+19
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. キューにはなるべく処理に必要な情報がセットで入っていてほしいと思います(あくまで私の場合)。したがって、キュー外での管理になっている
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なるほど、そのような実装もあるのですね。こちらを好む理由も理解しました。 |
||
| node = queue.popleft() | ||
| values_by_level.append(node.val) | ||
| for child in (node.left, node.right): | ||
| if child is not None: | ||
| queue.append(child) | ||
| level_order.append(values_by_level) | ||
| return level_order | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
これ、素直に対応するループに直せますか。再帰呼ぶかわりにスタックにやることを積んで dfs にするということです。(再帰とループの変形をすること自体が選択として見えているかが気にかかりました。)
再帰は制限が多いので。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.uvguf4c3q02d
再帰をループに
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.deivkzaqvetb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
新しく書きました。pythonのsys.setrecursionlimitなど知らなかったので勉強になりました。
https://docs.python.org/3/library/sys.html#sys.setrecursionlimit