-
Notifications
You must be signed in to change notification settings - Fork 0
Solved Arai60/98. Validate Binary Search Tree #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
## Step 1. Initial Solution | ||
|
||
- 初めに思い付いたのは左右との比較を行い、どんどん下りていく方法 | ||
- ただ、直後のノードを確かめるだけでは不十分なので毎回深掘る実装をする | ||
- 時間計算量は最大O(n^2)になってしまうので流石に修正する | ||
|
||
```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)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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 | ||
``` | ||
|
||
- 一番左から順番に最小値を更新していって確かめる方法もある | ||
- あまりそういう発想はなかったが、リストに並べるのとやってることは一緒 | ||
- 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. とても読みやすかったです。 |
||
``` |
There was a problem hiding this comment.
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