Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions _experiment.py
Original file line number Diff line number Diff line change
@@ -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
249 changes: 249 additions & 0 deletions memo.md
Original file line number Diff line number Diff line change
@@ -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
Comment on lines +49 to +53
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

フラグを使わないほうがシンプルだと思います。

if node.left is not None:
    if not is_valid_bst_helper(node.left, minimum_val, node.val):
        return False

if node.right is not None:
    if not is_valid_bst_helper(node.right, node.val, maximum_val):
        return False

return True

また、関数の先頭で None をチェックすることで、もう少しシンプルにできそうです。

def isValidBST(self, root: Optional[TreeNode]) -> bool:
    def is_valid_bst_helper(node: TreeNode, minimum_val: int, maximum_val: int) -> bool:
        if node is None:
            return True

        if not (minimum_val < node.val < maximum_val):
            return False

        if not is_valid_bst_helper(node.left, minimum_val, node.val):
            return False

        if not is_valid_bst_helper(node.right, node.val, maximum_val):
            return False

        return True

    return is_valid_bst_helper(root, float("-inf"), float("inf"))

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
Comment on lines +202 to +204
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(あまりコメントできるところないのですが、強いてコメントすれば)
for 文とwhile文を抜けるところに空行を入れると可読性は上がるかもしれません
https://peps.python.org/pep-0008/#blank-lines

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

まあ、これは趣味の範囲ですかね。


```

# 他の人のコードやコメント

* 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
```
26 changes: 26 additions & 0 deletions step1-1_recursion.py
Original file line number Diff line number Diff line change
@@ -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"))
23 changes: 23 additions & 0 deletions step1-2_dfs.py
Original file line number Diff line number Diff line change
@@ -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
32 changes: 32 additions & 0 deletions step1-3_bfs.py
Original file line number Diff line number Diff line change
@@ -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


20 changes: 20 additions & 0 deletions step2-1_recursion.py
Original file line number Diff line number Diff line change
@@ -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"))
21 changes: 21 additions & 0 deletions step2-2_dfs.py
Original file line number Diff line number Diff line change
@@ -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
Loading