diff --git a/111/memo.md b/111/memo.md new file mode 100644 index 0000000..ce31516 --- /dev/null +++ b/111/memo.md @@ -0,0 +1,27 @@ +# 111. Minimum Depth of Binary Tree + +- https://discord.com/channels/1084280443945353267/1196472827457589338/1237988315781664770 +> はい。再帰とスタックの書き換えくらいだと私も思います。しかし、この二つの変換が息を吐くようにできるとだいぶ分かっている感じがするので大事だと思います。 + +> そうですね。木の高さを求めるときには、上から数字を配っていくか、下から集めてくるかの2方向があって、それぞれ再帰で書くか、スタックとループで書くか、がありますね。 + +- sol1.py: トップダウン + ループ(BFS) + - 早期終了でき、深すぎる入力を避けられる。 + - これが一番良いのでは? +- sol2.py: ボトムアップ + 再帰(DFS) + - 深い入力があった場合にオーバーフロー +- sol3.py: トップダウン + 再帰(DFS) + - 根からの情報を配りやすい(パスの条件が追加されるなど) + - 深い入力があった場合にオーバーフロー(上と同様) +- sol4.py: ボトムアップ + ループ(DFS / スタック) + - postorderで探索したいけど再帰を避けたい場合に使える +- もっとスラスラ書けるようになりたい + +## BFS / DFS +- **BFS(幅優先)**: 根から浅い順に探索。最初に見つけた葉が最小深さなので「葉を見つけたら即 return」ができる(`sol1.py`)。 +- **DFS(深さ優先)**: 全探索しつつ最小を取るか、ボトムアップで子の答えを集約する(`sol2.py`, `sol3.py`, `sol4.py`)。 + +## トップダウン / ボトムアップ(木DPの向き) +- **トップダウン**: 引数で深さを配る(`dfs(node, depth)` みたいに進む)。葉で答えを更新する(`sol3.py`)。 +- **ボトムアップ**: 子の答えを先に確定してから親を計算する(`sol2.py`, `sol4.py`)。 + diff --git a/111/sol1.py b/111/sol1.py new file mode 100644 index 0000000..4d2fa28 --- /dev/null +++ b/111/sol1.py @@ -0,0 +1,24 @@ +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 minDepth(self, root: Optional[TreeNode]) -> int: + if root is None: + return 0 + frontier = deque() + frontier.append((1, root)) + while frontier: + depth, node = frontier.popleft() + if node.right is None and node.left is None: + return depth + for child in (node.left, node.right): + if child is None: + continue + frontier.append((depth + 1, child)) + raise RuntimeError("unreachable") diff --git a/111/sol2.py b/111/sol2.py new file mode 100644 index 0000000..298d75a --- /dev/null +++ b/111/sol2.py @@ -0,0 +1,18 @@ +# 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 minDepth(self, root: Optional[TreeNode]) -> int: + if root is None: + return 0 + if root.right is None and root.left is None: + return 1 + min_depth = float("inf") + for child in (root.right, root.left): + if child is None: + continue + min_depth = min(min_depth, self.minDepth(child) + 1) + return min_depth diff --git a/111/sol3.py b/111/sol3.py new file mode 100644 index 0000000..0ff4400 --- /dev/null +++ b/111/sol3.py @@ -0,0 +1,24 @@ +# 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 minDepth(self, root: Optional[TreeNode]) -> int: + if root is None: + return 0 + min_depth = float("inf") + + def traverse(node, depth): + nonlocal min_depth + if node.left is None and node.right is None: + min_depth = min(min_depth, depth) + return + for child in (node.left, node.right): + if child is None: + continue + traverse(child, depth + 1) + + traverse(root, 1) + return min_depth diff --git a/111/sol4.py b/111/sol4.py new file mode 100644 index 0000000..8e83044 --- /dev/null +++ b/111/sol4.py @@ -0,0 +1,36 @@ +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 minDepth(self, root: Optional[TreeNode]) -> int: + if root is None: + return 0 + node_to_depth = {} + frontier = deque() + frontier.append((root, False)) + while frontier: + node, is_visited = frontier.pop() + if node is None: + continue + if not is_visited: + frontier.append((node, True)) + frontier.append((node.left, False)) + frontier.append((node.right, False)) + continue + depth_l = node_to_depth.get(node.left, None) + depth_r = node_to_depth.get(node.right, None) + if depth_l is None and depth_r is None: + node_to_depth[node] = 1 + elif depth_l is None: + node_to_depth[node] = depth_r + 1 + elif depth_r is None: + node_to_depth[node] = depth_l + 1 + else: + node_to_depth[node] = min(depth_l, depth_r) + 1 + return node_to_depth[root]