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..fb50f99 100644 --- a/memo.md +++ b/memo.md @@ -1 +1,250 @@ # 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-3 (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 +``` + +# 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 + +``` + +# 他の人のコードやコメント + +* 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 + +```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/step1-1_recursion.py b/step1-1_recursion.py new file mode 100644 index 0000000..13e1b08 --- /dev/null +++ b/step1-1_recursion.py @@ -0,0 +1,26 @@ +# 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 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 diff --git a/step3-2_dfs.py b/step3-2_dfs.py new file mode 100644 index 0000000..d1efb1e --- /dev/null +++ b/step3-2_dfs.py @@ -0,0 +1,25 @@ +# 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