From 4520719188cd6b886e7b569087f1c5e1811596d7 Mon Sep 17 00:00:00 2001 From: tom4649 Date: Mon, 16 Mar 2026 07:14:16 +0900 Subject: [PATCH 1/3] 111. Minimum Depth of Binary Tree --- 111/memo.md | 21 +++++++++++++++++++++ 111/sol1.py | 24 ++++++++++++++++++++++++ 111/sol2.py | 18 ++++++++++++++++++ 111/sol3.py | 27 +++++++++++++++++++++++++++ 111/sol4.py | 31 +++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+) create mode 100644 111/memo.md create mode 100644 111/sol1.py create mode 100644 111/sol2.py create mode 100644 111/sol3.py create mode 100644 111/sol4.py diff --git a/111/memo.md b/111/memo.md new file mode 100644 index 0000000..dc695d6 --- /dev/null +++ b/111/memo.md @@ -0,0 +1,21 @@ +# 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 / スタック) +- もっとスラスラ書けるようになりたい + +## 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..e7abef2 --- /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 + que = deque() + que.append((1, root)) + while que: + depth, node = que.popleft() + if node.right is None and node.left is None: + return depth + for child in [node.right, node.left]: + if child is None: + continue + que.append((depth + 1, child)) + raise ValueError 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..7a356f8 --- /dev/null +++ b/111/sol3.py @@ -0,0 +1,27 @@ +# 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: + min_depth = float("inf") + if root is None: + return 0 + + def dfs(node, depth): + nonlocal min_depth + if node is None: + return + 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 + dfs(child, depth + 1) + return + + dfs(root, 1) + return min_depth diff --git a/111/sol4.py b/111/sol4.py new file mode 100644 index 0000000..f897300 --- /dev/null +++ b/111/sol4.py @@ -0,0 +1,31 @@ +from collections import defaultdict, 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: + node_to_depth = defaultdict(int) + visited_nodes = set() + que = deque() + que.append((root, False)) + while que: + node, is_visited = que.pop() + if node is None: + continue + if is_visited: + depth_l = node_to_depth.get(node.left, 0) + depth_r = node_to_depth.get(node.right, 0) + if depth_l == 0 or depth_r == 0: + node_to_depth[node] = max(depth_l, depth_r) + 1 + else: + node_to_depth[node] = min(depth_l, depth_r) + 1 + else: + que.append((node, True)) + que.append((node.left, False)) + que.append((node.right, False)) + return node_to_depth[root] From 6c2690105fe4dfb8944fc4bfb09ce637ca051cdd Mon Sep 17 00:00:00 2001 From: tom4649 Date: Mon, 16 Mar 2026 09:00:26 +0900 Subject: [PATCH 2/3] fix: adjust error handling --- 111/sol1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/111/sol1.py b/111/sol1.py index e7abef2..f91fadb 100644 --- a/111/sol1.py +++ b/111/sol1.py @@ -21,4 +21,4 @@ def minDepth(self, root: Optional[TreeNode]) -> int: if child is None: continue que.append((depth + 1, child)) - raise ValueError + raise RuntimeError("unreachable") From b7b226a97ab79f20f82d54ebab347c34f783d534 Mon Sep 17 00:00:00 2001 From: tom4649 Date: Thu, 26 Mar 2026 08:45:55 +0900 Subject: [PATCH 3/3] Add suggested changes --- 111/memo.md | 6 ++++++ 111/sol1.py | 12 ++++++------ 111/sol3.py | 11 ++++------- 111/sol4.py | 39 ++++++++++++++++++++++----------------- 4 files changed, 38 insertions(+), 30 deletions(-) diff --git a/111/memo.md b/111/memo.md index dc695d6..ce31516 100644 --- a/111/memo.md +++ b/111/memo.md @@ -6,9 +6,15 @@ > そうですね。木の高さを求めるときには、上から数字を配っていくか、下から集めてくるかの2方向があって、それぞれ再帰で書くか、スタックとループで書くか、がありますね。 - sol1.py: トップダウン + ループ(BFS) + - 早期終了でき、深すぎる入力を避けられる。 + - これが一番良いのでは? - sol2.py: ボトムアップ + 再帰(DFS) + - 深い入力があった場合にオーバーフロー - sol3.py: トップダウン + 再帰(DFS) + - 根からの情報を配りやすい(パスの条件が追加されるなど) + - 深い入力があった場合にオーバーフロー(上と同様) - sol4.py: ボトムアップ + ループ(DFS / スタック) + - postorderで探索したいけど再帰を避けたい場合に使える - もっとスラスラ書けるようになりたい ## BFS / DFS diff --git a/111/sol1.py b/111/sol1.py index f91fadb..4d2fa28 100644 --- a/111/sol1.py +++ b/111/sol1.py @@ -11,14 +11,14 @@ class Solution: def minDepth(self, root: Optional[TreeNode]) -> int: if root is None: return 0 - que = deque() - que.append((1, root)) - while que: - depth, node = que.popleft() + 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.right, node.left]: + for child in (node.left, node.right): if child is None: continue - que.append((depth + 1, child)) + frontier.append((depth + 1, child)) raise RuntimeError("unreachable") diff --git a/111/sol3.py b/111/sol3.py index 7a356f8..0ff4400 100644 --- a/111/sol3.py +++ b/111/sol3.py @@ -6,22 +6,19 @@ # self.right = right class Solution: def minDepth(self, root: Optional[TreeNode]) -> int: - min_depth = float("inf") if root is None: return 0 + min_depth = float("inf") - def dfs(node, depth): + def traverse(node, depth): nonlocal min_depth - if node is None: - return 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 - dfs(child, depth + 1) - return + traverse(child, depth + 1) - dfs(root, 1) + traverse(root, 1) return min_depth diff --git a/111/sol4.py b/111/sol4.py index f897300..8e83044 100644 --- a/111/sol4.py +++ b/111/sol4.py @@ -1,4 +1,4 @@ -from collections import defaultdict, deque +from collections import deque # Definition for a binary tree node. @@ -9,23 +9,28 @@ # self.right = right class Solution: def minDepth(self, root: Optional[TreeNode]) -> int: - node_to_depth = defaultdict(int) - visited_nodes = set() - que = deque() - que.append((root, False)) - while que: - node, is_visited = que.pop() + 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 is_visited: - depth_l = node_to_depth.get(node.left, 0) - depth_r = node_to_depth.get(node.right, 0) - if depth_l == 0 or depth_r == 0: - node_to_depth[node] = max(depth_l, depth_r) + 1 - else: - node_to_depth[node] = min(depth_l, depth_r) + 1 + 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: - que.append((node, True)) - que.append((node.left, False)) - que.append((node.right, False)) + node_to_depth[node] = min(depth_l, depth_r) + 1 return node_to_depth[root]