From a04529828de8c72e02b6143c5279d99a10351f96 Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Fri, 27 Mar 2026 13:49:59 +0900 Subject: [PATCH 1/5] step1 --- experiment.py | 12 +++++ memo.md | 125 +++++++++++++++++++++++++++++++++++++++++++ step1-1_recursion.py | 29 ++++++++++ step1-2_dfs.py | 23 ++++++++ step1-3_bfs.py | 32 +++++++++++ 5 files changed, 221 insertions(+) create mode 100644 experiment.py create mode 100644 step1-1_recursion.py create mode 100644 step1-2_dfs.py create mode 100644 step1-3_bfs.py diff --git a/experiment.py b/experiment.py new file mode 100644 index 0000000..1f617fd --- /dev/null +++ b/experiment.py @@ -0,0 +1,12 @@ +import sys + + +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + +node = TreeNode() +print(sys.getsizeof(node)) # 48 +print(sys.getsizeof(node.__dict__)) # 296 \ No newline at end of file diff --git a/memo.md b/memo.md index 4bd0397..0fad61c 100644 --- a/memo.md +++ b/memo.md @@ -1 +1,126 @@ # Step1 + +## アプローチ + +* 再帰呼び出しでやる + * ざっくり方針 + * 自分の左に対して、仕事を指示 + * 自分の右に対して、仕事を指示 + * 自分の左と右がそれぞれ正しいかどうかをみる仕事を与えられる + * 左の結果と右の結果がただしいなら、上司に正しいと教える + * 計算量 + * すべてのノードを訪れるのでO(N) + * step数は10^4程度だとすると, 10^4 / 10^6 = 0.01 sec程度 + * メモリは再帰のスタック分必要 + * 最大で(左に偏り続ける木の場合で)10^4回の再帰が行われる可能性がある +* stackを使ったDFSでやる場合 + * pre-orderになる + * 処理候補をstackに積むときに, 親の情報と自分が小さくあるべきか大きくあるべきかを伝える +* queをつかったBFSでやる場合 + * DFS同様に,処理候補をqueに入れるときに, 親の情報と自分が小さくあるべきかどうかを伝える +* DFSかBFSかは与えられた木の構造やパソコンのメモリ状況で変わる + * DFSが適している状況 + * 同レベルのノード数が多すぎてメモリに乗らない時 + * 例えば、パソコンの残りメモリが8GBだとする + * ノードを一回作成するたびに新たに消費されるバイト数 => 300 byteと見積もる + * classの参照(PyTypeObject) = 8 bytes + * attibuteの参照を集めたdictへの参照 296 bytes + * attributeの参照先を新たにオブジェクトとして作る場合はそのオブジェクトのサイズ + * 300 * x = 8 * 1024 * 1024 * 1024なので x = 3 * 10^7 + * 思ったよりも少なかった + * BFSが適している状況 + * 与えられた木が左に偏りすぎていてノードの深さ分メモリに乗り切らないとBFSが良い + +## Code1-1 (Recursion) + +```python +# solved 10:01 +# 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 isValidBST(self, root: Optional[TreeNode]) -> bool: + if root is None: + return True + + def is_valid_bst_helper(node: TreeNode, minimum_val: int, maximum_val: int) -> bool: + if not (minimum_val < node.val < maximum_val): + return False + + is_left_subtree_valid = True + if node.left is not None: + is_left_subtree_valid = is_valid_bst_helper(node.left, minimum_val, node.val) + + is_right_subtree_valid = True + if node.right is not None: + is_right_subtree_valid = is_valid_bst_helper(node.right, node.val, maximum_val) + + return is_left_subtree_valid and is_right_subtree_valid + + return is_valid_bst_helper(root, float("-inf"), float("inf")) +``` +## Code1-2 (DFS) + +```python +# solved 1:34 + +# 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 isValidBST(self, root: Optional[TreeNode]) -> bool: + if root is None: + return True + + node_to_visit = [(root, float("-inf"), float("inf"))] + while node_to_visit: + node, minimum_val, maximum_val = node_to_visit.pop() + if not (minimum_val < node.val < maximum_val): + return False + if node.left is not None: + node_to_visit.append((node.left, minimum_val, node.val)) + if node.right is not None: + node_to_visit.append((node.right, node.val, maximum_val)) + return True +``` + +## Code1-1 (BFS) + +```python +# solved 2:38 +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 isValidBST(self, root: Optional[TreeNode]) -> bool: + if root is None: + return True + + frontier = deque([(root, float("-inf"), float("inf"))]) + while frontier: + next_frontier = deque() + while frontier: + node, minimum_val, maximum_val = frontier.popleft() + if not (minimum_val < node.val < maximum_val): + return False + if node.left is not None: + next_frontier.append((node.left, minimum_val, node.val)) + if node.right is not None: + next_frontier.append((node.right, node.val, maximum_val)) + frontier = next_frontier + return True + + +``` \ No newline at end of file diff --git a/step1-1_recursion.py b/step1-1_recursion.py new file mode 100644 index 0000000..173083a --- /dev/null +++ b/step1-1_recursion.py @@ -0,0 +1,29 @@ +# solved 10:01 + + +# 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 isValidBST(self, root: Optional[TreeNode]) -> bool: + if root is None: + return True + + def is_valid_bst_helper(node: TreeNode, minimum_val: int, maximum_val: int) -> bool: + if not (minimum_val < node.val < maximum_val): + return False + + is_left_subtree_valid = True + if node.left is not None: + is_left_subtree_valid = is_valid_bst_helper(node.left, minimum_val, node.val) + + is_right_subtree_valid = True + if node.right is not None: + is_right_subtree_valid = is_valid_bst_helper(node.right, node.val, maximum_val) + + return is_left_subtree_valid and is_right_subtree_valid + + return is_valid_bst_helper(root, float("-inf"), float("inf")) \ No newline at end of file diff --git a/step1-2_dfs.py b/step1-2_dfs.py new file mode 100644 index 0000000..909eeb9 --- /dev/null +++ b/step1-2_dfs.py @@ -0,0 +1,23 @@ +# solved 1:34 + +# 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 isValidBST(self, root: Optional[TreeNode]) -> bool: + if root is None: + return True + + node_to_visit = [(root, float("-inf"), float("inf"))] + while node_to_visit: + node, minimum_val, maximum_val = node_to_visit.pop() + if not (minimum_val < node.val < maximum_val): + return False + if node.left is not None: + node_to_visit.append((node.left, minimum_val, node.val)) + if node.right is not None: + node_to_visit.append((node.right, node.val, maximum_val)) + return True \ No newline at end of file diff --git a/step1-3_bfs.py b/step1-3_bfs.py new file mode 100644 index 0000000..4e72ee6 --- /dev/null +++ b/step1-3_bfs.py @@ -0,0 +1,32 @@ +# solved 2:38 + + +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 isValidBST(self, root: Optional[TreeNode]) -> bool: + if root is None: + return True + + frontier = deque([(root, float("-inf"), float("inf"))]) + while frontier: + next_frontier = deque() + while frontier: + node, minimum_val, maximum_val = frontier.popleft() + if not (minimum_val < node.val < maximum_val): + return False + if node.left is not None: + next_frontier.append((node.left, minimum_val, node.val)) + if node.right is not None: + next_frontier.append((node.right, node.val, maximum_val)) + frontier = next_frontier + return True + + \ No newline at end of file From 2dc25381d333dd4d1b86950fae18cd7dd4d6c8e2 Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Fri, 27 Mar 2026 14:22:32 +0900 Subject: [PATCH 2/5] step2 --- experiment.py => _experiment.py | 0 memo.md | 84 ++++++++++++++++++++++++++++++++- step1-1_recursion.py | 3 -- step2-1_recursion.py | 20 ++++++++ step2-2_dfs.py | 21 +++++++++ step2-3_bfs.py | 23 +++++++++ 6 files changed, 146 insertions(+), 5 deletions(-) rename experiment.py => _experiment.py (100%) create mode 100644 step2-1_recursion.py create mode 100644 step2-2_dfs.py create mode 100644 step2-3_bfs.py diff --git a/experiment.py b/_experiment.py similarity index 100% rename from experiment.py rename to _experiment.py diff --git a/memo.md b/memo.md index 0fad61c..9cfd5e2 100644 --- a/memo.md +++ b/memo.md @@ -90,7 +90,7 @@ class Solution: return True ``` -## Code1-1 (BFS) +## Code1-3 (BFS) ```python # solved 2:38 @@ -121,6 +121,86 @@ class Solution: next_frontier.append((node.right, node.val, maximum_val)) frontier = next_frontier return True +``` + +# Step2 + +## Code2-1 (Recursion) + +```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 isValidBST(self, root: TreeNode | None) -> bool: + if root is None: + return True + + def is_valid_bst_helper(node: TreeNode, minimum_val: int, maximum_val: int) -> bool: + if not (minimum_val < node.val < maximum_val): + return False + + is_left_subtree_valid = is_valid_bst_helper(node.left, minimum_val, node.val) if node.left is not None else True + is_right_subtree_valid = is_valid_bst_helper(node.right, node.val, maximum_val) if node.right is not None else True + return is_left_subtree_valid and is_right_subtree_valid + + return is_valid_bst_helper(root, float("-inf"), float("inf")) +``` + +## Code2-2 (DFS) + +```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 isValidBST(self, root: TreeNode | None) -> bool: + if root is None: + return True + + node_and_valid_range_pairs = [(root, float("-inf"), float("inf"))] + while node_and_valid_range_pairs: + node, minimum_val, maximum_val = node_and_valid_range_pairs.pop() + if not (minimum_val < node.val < maximum_val): + return False + if node.left is not None: + node_and_valid_range_pairs.append((node.left, minimum_val, node.val)) + if node.right is not None: + node_and_valid_range_pairs.append((node.right, node.val, maximum_val)) + return True +``` + +## Code2-3 (BFS) + +```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 isValidBST(self, root: TreeNode | None) -> bool: + if root is None: + return True + + frontier = [(root, float("-inf"), float("inf"))] + while frontier: + next_frontier = [] + for node, minimum_val, maximum_val in frontier: + if not (minimum_val < node.val < maximum_val): + return False + if node.left is not None: + next_frontier.append((node.left, minimum_val, node.val)) + if node.right is not None: + next_frontier.append((node.right, node.val, maximum_val)) + frontier = next_frontier + return True - ``` \ No newline at end of file diff --git a/step1-1_recursion.py b/step1-1_recursion.py index 173083a..13e1b08 100644 --- a/step1-1_recursion.py +++ b/step1-1_recursion.py @@ -1,6 +1,3 @@ -# solved 10:01 - - # Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): diff --git a/step2-1_recursion.py b/step2-1_recursion.py new file mode 100644 index 0000000..4bd34bd --- /dev/null +++ b/step2-1_recursion.py @@ -0,0 +1,20 @@ +# 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 isValidBST(self, root: TreeNode | None) -> bool: + if root is None: + return True + + def is_valid_bst_helper(node: TreeNode, minimum_val: int, maximum_val: int) -> bool: + if not (minimum_val < node.val < maximum_val): + return False + + is_left_subtree_valid = is_valid_bst_helper(node.left, minimum_val, node.val) if node.left is not None else True + is_right_subtree_valid = is_valid_bst_helper(node.right, node.val, maximum_val) if node.right is not None else True + return is_left_subtree_valid and is_right_subtree_valid + + return is_valid_bst_helper(root, float("-inf"), float("inf")) \ No newline at end of file diff --git a/step2-2_dfs.py b/step2-2_dfs.py new file mode 100644 index 0000000..78ef857 --- /dev/null +++ b/step2-2_dfs.py @@ -0,0 +1,21 @@ +# 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 isValidBST(self, root: TreeNode | None) -> bool: + if root is None: + return True + + node_and_valid_range_pairs = [(root, float("-inf"), float("inf"))] + while node_and_valid_range_pairs: + node, minimum_val, maximum_val = node_and_valid_range_pairs.pop() + if not (minimum_val < node.val < maximum_val): + return False + if node.left is not None: + node_and_valid_range_pairs.append((node.left, minimum_val, node.val)) + if node.right is not None: + node_and_valid_range_pairs.append((node.right, node.val, maximum_val)) + return True \ No newline at end of file diff --git a/step2-3_bfs.py b/step2-3_bfs.py new file mode 100644 index 0000000..01407d2 --- /dev/null +++ b/step2-3_bfs.py @@ -0,0 +1,23 @@ +# 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 isValidBST(self, root: TreeNode | None) -> bool: + if root is None: + return True + + frontier = [(root, float("-inf"), float("inf"))] + while frontier: + next_frontier = [] + for node, minimum_val, maximum_val in frontier: + if not (minimum_val < node.val < maximum_val): + return False + if node.left is not None: + next_frontier.append((node.left, minimum_val, node.val)) + if node.right is not None: + next_frontier.append((node.right, node.val, maximum_val)) + frontier = next_frontier + return True From 1e8b6e3987b50e35a974c91cb5b3aeaae2395297 Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Fri, 27 Mar 2026 14:38:21 +0900 Subject: [PATCH 3/5] step2 comment --- memo.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/memo.md b/memo.md index 9cfd5e2..055af1c 100644 --- a/memo.md +++ b/memo.md @@ -203,4 +203,20 @@ class Solution: frontier = next_frontier return True -``` \ No newline at end of file +``` + +# 他の人のコードやコメント + +* https://github.com/mamo3gr/arai60/pull/26/files + * stackやyieldを使って, Pre-orderだけじゃなくて, In-orderも実装している + * In-orderで見たときに, BSTは昇順に並ぶ性質を利用 + + +* `maximum_val`や`minimum_val`はその値を含むのかどうかわからないので, `upper_bound_exclusive`などにしたい + * https://github.com/tom4649/Coding/pull/27#discussion_r2963092528 +* そもそも, `minimum`や`maximum`という命名が微妙 + * https://github.com/dxxsxsxkx/leetcode/pull/28#discussion_r2752202157 + * > min_value, max_valueがなんの最小値・最大値なのか読み取るのに苦労しました。部分木の最小・最大値でもありませんし、とってもいい値の最小・最大でもない (とっても良い値は min_value < x < max_value : exclusive) なので、やや語弊があるかなと思います。 + + +# Step3 \ No newline at end of file From fc3a909f8124ca95691da702c435521d8d928d15 Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Fri, 27 Mar 2026 14:43:28 +0900 Subject: [PATCH 4/5] step3 1st --- step3-2_dfs.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 step3-2_dfs.py diff --git a/step3-2_dfs.py b/step3-2_dfs.py new file mode 100644 index 0000000..769ae61 --- /dev/null +++ b/step3-2_dfs.py @@ -0,0 +1,24 @@ +# 1st: 1:29 + +# 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 isValidBST(self, root: Optional[TreeNode]) -> bool: + if root is None: + return True + + node_and_valid_range_pairs = [(root, float("-inf"), float("inf"))] + while node_and_valid_range_pairs: + node, lower_bound_exclusive, upper_bound_exclusive = node_and_valid_range_pairs.pop() + if not (lower_bound_exclusive < node.val < upper_bound_exclusive): + return False + if node.left is not None: + node_and_valid_range_pairs.append((node.left, lower_bound_exclusive, node.val)) + if node.right is not None: + node_and_valid_range_pairs.append((node.right, node.val, upper_bound_exclusive)) + return True + \ No newline at end of file From fa6cc91dea02c91214cd0f5c367c2862f2faabc6 Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Fri, 27 Mar 2026 14:47:03 +0900 Subject: [PATCH 5/5] step3 memo --- memo.md | 30 +++++++++++++++++++++++++++++- step3-2_dfs.py | 17 +++++++++-------- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/memo.md b/memo.md index 055af1c..fb50f99 100644 --- a/memo.md +++ b/memo.md @@ -219,4 +219,32 @@ class Solution: * > min_value, max_valueがなんの最小値・最大値なのか読み取るのに苦労しました。部分木の最小・最大値でもありませんし、とってもいい値の最小・最大でもない (とっても良い値は min_value < x < max_value : exclusive) なので、やや語弊があるかなと思います。 -# Step3 \ No newline at end of file +# Step3 + +```python +# 1st: 1:29 +# 2nd: 1:32 +# 3rd: 1:16 + +# 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 isValidBST(self, root: Optional[TreeNode]) -> bool: + if root is None: + return True + + node_and_valid_bounds = [(root, float("-inf"), float("inf"))] + while node_and_valid_bounds: + node, lower_bound_exclusive, upper_bound_exclusive = node_and_valid_bounds.pop() + if not (lower_bound_exclusive < node.val < upper_bound_exclusive): + return False + if node.left is not None: + node_and_valid_bounds.append((node.left, lower_bound_exclusive, node.val)) + if node.right is not None: + node_and_valid_bounds.append((node.right, node.val, upper_bound_exclusive)) + return True +``` \ No newline at end of file diff --git a/step3-2_dfs.py b/step3-2_dfs.py index 769ae61..d1efb1e 100644 --- a/step3-2_dfs.py +++ b/step3-2_dfs.py @@ -1,4 +1,6 @@ # 1st: 1:29 +# 2nd: 1:32 +# 3rd: 1:16 # Definition for a binary tree node. # class TreeNode: @@ -10,15 +12,14 @@ class Solution: def isValidBST(self, root: Optional[TreeNode]) -> bool: if root is None: return True - - node_and_valid_range_pairs = [(root, float("-inf"), float("inf"))] - while node_and_valid_range_pairs: - node, lower_bound_exclusive, upper_bound_exclusive = node_and_valid_range_pairs.pop() + + node_and_valid_bounds = [(root, float("-inf"), float("inf"))] + while node_and_valid_bounds: + node, lower_bound_exclusive, upper_bound_exclusive = node_and_valid_bounds.pop() if not (lower_bound_exclusive < node.val < upper_bound_exclusive): return False if node.left is not None: - node_and_valid_range_pairs.append((node.left, lower_bound_exclusive, node.val)) + node_and_valid_bounds.append((node.left, lower_bound_exclusive, node.val)) if node.right is not None: - node_and_valid_range_pairs.append((node.right, node.val, upper_bound_exclusive)) - return True - \ No newline at end of file + node_and_valid_bounds.append((node.right, node.val, upper_bound_exclusive)) + return True \ No newline at end of file