diff --git a/102/bench_list_vs_tuple.py b/102/bench_list_vs_tuple.py new file mode 100644 index 0000000..a910a59 --- /dev/null +++ b/102/bench_list_vs_tuple.py @@ -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() diff --git a/102/memo.md b/102/memo.md new file mode 100644 index 0000000..8c30985 --- /dev/null +++ b/102/memo.md @@ -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 diff --git a/102/sol1.py b/102/sol1.py new file mode 100644 index 0000000..50550a4 --- /dev/null +++ b/102/sol1.py @@ -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: + 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 diff --git a/102/sol2.py b/102/sol2.py new file mode 100644 index 0000000..be171fd --- /dev/null +++ b/102/sol2.py @@ -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 diff --git a/102/sol2_loop.py b/102/sol2_loop.py new file mode 100644 index 0000000..a01503b --- /dev/null +++ b/102/sol2_loop.py @@ -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 diff --git a/102/sol3.py b/102/sol3.py new file mode 100644 index 0000000..772731e --- /dev/null +++ b/102/sol3.py @@ -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): + 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