Skip to content
Open
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
107 changes: 107 additions & 0 deletions Python3/98. Validate Binary Search Tree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
## Step 1. Initial Solution

- 初めに思い付いたのは左右との比較を行い、どんどん下りていく方法
- ただ、直後のノードを確かめるだけでは不十分なので毎回深掘る実装をする
- 時間計算量は最大O(n^2)になってしまうので流石に修正する
Copy link

Choose a reason for hiding this comment

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

線形時間にする方法として「ペアを返す再帰にすると線形時間になる」を使う方法もあります。
再帰が (最小、最大、is_valid) の3つ組を返すようにするということです。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.r1pr4pvhjsjx


```python
class Solution:
def isValidBST(self, root: Optional[TreeNode]) -> bool:
def minInSubtree(node: TreeNode) -> int:
while node.left is not None:
node = node.left
return node.val

def maxInSubtree(node: TreeNode) -> int:
while node.right is not None:
node = node.right
return node.val

if root is None:
return True
if root.left is not None and root.val <= maxInSubtree(root.left):
return False
if root.right is not None and root.val >= minInSubtree(root.right):
return False
return self.isValidBST(root.left) and self.isValidBST(root.right)
```

- min, maxを保持すれば下りながらチェックが出来る
- min, maxの更新の仕方に少し手こずった

```python
MIN_VAL = - 2**31 - 1
MAX_VAL = 2**31
class Solution:
def isValidBST(self, root: Optional[TreeNode]) -> bool:
def isInRange(node: Optional[TreeNode], min_val: int, max_val: int) -> bool:
if node is None:
return True
if not min_val < node.val < max_val:
return False
return isInRange(node.left, min_val, node.val) \
and isInRange(node.right, node.val, max_val)
return isInRange(root, MIN_VAL, MAX_VAL)
```

### Complexity Analysis

- 時間計算量:O(n)
- 空間計算量:O(n)
- 最大でもO(n)
- 深さ分のメモリのみ

## Step 2. Alternatives

- iterationで実装した場合

```python
MIN_VAL = - 2**31 - 1
MAX_VAL = 2**31
class Solution:
def isValidBST(self, root: Optional[TreeNode]) -> bool:
nodes_and_ranges: list[tuple[int]] = [(root, MIN_VAL, MAX_VAL)]

Choose a reason for hiding this comment

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

MIN_VAL、MAX_VALを定義せず、-sys.maxint - 1sys.maxintを使って初期化しても、意図は伝わるかと思いました。

while nodes_and_ranges:
node, min_val, max_val = nodes_and_ranges.pop()
if node is None:
continue
if not min_val < node.val < max_val:
return False
nodes_and_ranges.append((node.left, min_val, node.val))
nodes_and_ranges.append((node.right, node.val, max_val))
return True
```

- 一番左から順番に最小値を更新していって確かめる方法もある
- あまりそういう発想はなかったが、リストに並べるのとやってることは一緒
- https://github.com/olsen-blue/Arai60/pull/28/files#diff-00bae6eb77a3ded9691eb5fcf3e550a934f45ff6cd54caa925f1318ee161d1f7R120
- リストを作る代わりにyieldを使う手もある
- https://discord.com/channels/1084280443945353267/1192736784354918470/1235116690661179465
- こういうiterableの作り方があるとは知らなかった
- 再帰がシンプルで分かりやすいが、今回はiterationの方が深さを気にする必要もなく安全そうなので好み
- infを用いると型が違うのがネック
- https://github.com/Mike0121/LeetCode/pull/8/files#diff-da307758356182a323f52d9c98e53cd8ce7dd7e3c7793d613dc2121a69ea24adR16
- 個人的には自分でMIN_VAL, MAX_VALを定義してしまいたいと思ったが保守性は下がりそうで難しい

## Step 3. Final Solution

- 型定義が長すぎて逆に可読性が下がっていそう
- 個人的には分かりやすさだけでなく型チェックを自動化するためにもあった方が良いという理解だがここまでやる必要はないかもしれない

```python
MIN_VAL = - 2**31 - 1
MAX_VAL = 2**31

class Solution:
def isValidBST(self, root: Optional[TreeNode]) -> bool:
nodes_and_ranges: list[tuple[Optional[TreeNode], int, int]] = [(root, MIN_VAL, MAX_VAL)]
while nodes_and_ranges:
node, min_val, max_val = nodes_and_ranges.pop()
if node is None:
continue
if not min_val < node.val < max_val:
return False
nodes_and_ranges.append((node.left, min_val, node.val))
nodes_and_ranges.append((node.right, node.val, max_val))
return True

Choose a reason for hiding this comment

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

とても読みやすかったです。
Generatorを使った実装は、inorder順がソート順になるという特徴を利用していて直感的なので、練習してみると良いと思います。

```