-
Notifications
You must be signed in to change notification settings - Fork 0
solved Validate Binary Search Tree #41
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: arai60
Are you sure you want to change the base?
Changes from all commits
e3856f9
fa0fa07
69fa435
e585107
06596cd
f92fb21
d2f7cfa
04f9173
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,47 @@ | ||
| # 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 | ||
|
|
||
| # テストケースで親ノードが及ぼす制約を考慮できていなく弾かれる | ||
| # なんだか命名やら条件文の書き方が不安 | ||
| # 空間計算量と時間計算量ともにノード数をNとしたときにO(N) | ||
| # 今回の最大ノード数は10^4なのでPythonの1秒間の実行数が10^7程度だった記憶があるので(間違ってたら指摘してください)10^-3秒程度で実行が終わる | ||
| # そういえば, 今回はスタックオーバーフローが起こらなかったが, Pythonのデフォルトでの最大再帰数を考えると最大ノード数は10^4なので片方に偏ったBinary Treeの場合再帰エラーがおきる | ||
|
|
||
| class Solution: | ||
| def isValidBST(self, root: Optional[TreeNode]) -> bool: | ||
| # -2 ** 31 <= Node.val <= 2 ** 31 - 1なので開区間でとる | ||
| MIN = -2 ** 31 - 1 | ||
| MAX = 2 ** 32 | ||
|
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. このMAXの2^32は、2^31の間違いですか?
Owner
Author
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. あ、ミスっとりますね...そうです |
||
| return self.isValidHelper(root, MIN, MAX) | ||
|
|
||
| def isValidHelper(self, node: Optional[TreeNode], left, right) -> bool: | ||
|
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. node.left と left が違う型のものなのがわりと混乱する感じがします。 |
||
| # left, rightは子に対する制約 left < node.child.val < rightであることを課す | ||
| # left, rightは親の値によってきまる | ||
|
|
||
| if node.left and node.right: | ||
|
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. 子の値を親が確認している点にやや違和感を感じました。この関数は、 node.val のみをチェックし、子の val のチェックは再帰関数の呼び出し先で行うのが良いと思います。
Owner
Author
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. 子の値を親が確認しないように変更したものをphase4にupいたしました。 |
||
| if not (left < node.left.val < right and left < node.right.val < right): | ||
| return False | ||
| if node.left.val < node.val < node.right.val: | ||
| return self.isValidHelper(node.left, left, node.val) and self.isValidHelper(node.right, node.val, right) | ||
| return False | ||
|
|
||
| if node.left: | ||
| if not left < node.left.val < right: | ||
| return False | ||
| if node.left.val < node.val: | ||
| return self.isValidHelper(node.left, left, node.val) | ||
| return False | ||
|
|
||
| if node.right: | ||
| if not left < node.right.val < right: | ||
| return False | ||
| if node.val < node.right.val: | ||
| return self.isValidHelper(node.right, node.val, right) | ||
| return False | ||
|
|
||
| # ここまで到達すると葉ノードであることを示す | ||
| return True | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| # hroc135: https://github.com/hroc135/leetcode/pull/27/files | ||
| # lower_bound, upper_boundとして名前を採用, 末尾最適化の話を一応確認, やはりMIN, MAXのマジックナンバーはよくなかったかと思い, NULLの時も考慮した比較関数isWithinIntervalを用意 | ||
| # isValidChildなども名前の候補にあがったけどisValidHelperと紛らわしくタイポが発生したのでやめた | ||
| # goto-untrapped: https://github.com/goto-untrapped/Arai60/pull/52/files | ||
| # fhiyo: https://github.com/fhiyo/leetcode/pull/30/files | ||
| # inorderでやる方法, アルゴリズムイントロダクションでもやったので真っ先に思い浮かんだが親のポインタがないので面倒だなと思っていたがyieldを使えばいいことを学ぶ | ||
| # あとはnode.valをif文の左側にもってくるなどの修正を加えるなど | ||
|
|
||
|
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. 下から上に再帰するのもありかもしれませんね。
Owner
Author
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. phase5に解いてみました |
||
| class Solution: | ||
| def isValidBST(self, root: Optional[TreeNode]) -> bool: | ||
| return self.isValidHelper(root, None, None) | ||
|
|
||
| def isValidHelper(self, node: Optional[TreeNode], lower_bound: Optional[int], upper_bound: Optional[int]) -> bool: | ||
| # lower_bound, upper_boundは子に対する制約 lower_bound <= node.child.val <= upper_boundであることを課す | ||
| # lower_bound, upper_boundは親の値によってきまる | ||
| if node.left and node.right: | ||
| if not self.isWithinInterval(node.left, lower_bound, upper_bound) or not self.isWithinInterval(node.right, lower_bound, upper_bound): | ||
| return False | ||
| if node.left.val < node.val < node.right.val: | ||
| return self.isValidHelper(node.left, lower_bound, node.val) and self.isValidHelper(node.right, node.val, upper_bound) | ||
| return False | ||
|
|
||
| if node.left: | ||
| if not self.isWithinInterval(node.left, lower_bound, upper_bound): | ||
| return False | ||
| if node.val > node.left.val: | ||
| return self.isValidHelper(node.left, lower_bound, node.val) | ||
| return False | ||
|
|
||
| if node.right: | ||
| if not self.isWithinInterval(node.right, lower_bound, upper_bound): | ||
| return False | ||
| if node.val < node.right.val: | ||
| return self.isValidHelper(node.right, node.val, upper_bound) | ||
| return False | ||
| # ここまで到達すると葉ノードであることを示す | ||
| return True | ||
|
|
||
| def isWithinInterval(self, node: TreeNode, lower_bound: Optional[int], upper_bound: Optional[int]) -> bool: | ||
| if lower_bound and upper_bound: | ||
| return lower_bound < node.val < upper_bound | ||
|
|
||
| if lower_bound: | ||
| return node.val > lower_bound | ||
|
|
||
| if upper_bound: | ||
| return node.val < upper_bound | ||
| # root nodeの場合 | ||
| return True | ||
|
|
||
| # yieldを使ったinorder | ||
| class Solution: | ||
| def isValidBST(self, root: Optional[TreeNode]) -> bool: | ||
| def generate_node_inorder(node): | ||
| if not node: | ||
| return | ||
| yield from generate_node_inorder(node.left) | ||
| yield node | ||
| yield from generate_node_inorder(node.right) | ||
| # inorderで探索してソートされていなかったら二分木ではない | ||
| prev_val = None | ||
| for node in generate_node_inorder(root): | ||
| if prev_val is not None and prev_val >= node.val: | ||
| return False | ||
| prev_val = node.val | ||
| return True | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| class Solution: | ||
| def isValidBST(self, root: Optional[TreeNode]) -> bool: | ||
| return self.isValidBSTHelper(root, None, None) | ||
|
|
||
| def isValidBSTHelper(self, node: Optional[TreeNode], lower_bound: Optional[TreeNode], upper_bound: Optional[TreeNode]) -> bool: | ||
| if node.left and node.right: | ||
| if not self.isWithinIntervals(node.left, lower_bound, upper_bound) or not self.isWithinIntervals(node.right, lower_bound, upper_bound): | ||
| return False | ||
| if node.left.val < node.val < node.right.val: | ||
| return self.isValidBSTHelper(node.left, lower_bound, node.val) and self.isValidBSTHelper(node.right, node.val, upper_bound) | ||
| return False | ||
| if node.left: | ||
| if not self.isWithinIntervals(node.left, lower_bound, upper_bound): | ||
| return False | ||
| if node.val > node.left.val: | ||
| return self.isValidBSTHelper(node.left, lower_bound, node.val) | ||
| return False | ||
|
|
||
| if node.right: | ||
| if not self.isWithinIntervals(node.right, lower_bound, upper_bound): | ||
| return False | ||
| if node.val < node.right.val: | ||
| return self.isValidBSTHelper(node.right, node.val, upper_bound) | ||
| return False | ||
| # 葉ノードの場合 | ||
| return True | ||
|
|
||
| def isWithinIntervals(self, node: TreeNode, lower_bound: Optional[TreeNode], upper_bound: Optional[TreeNode]) -> bool: | ||
| if lower_bound and upper_bound: | ||
| return lower_bound < node.val < upper_bound | ||
|
|
||
| if lower_bound: | ||
| return node.val > lower_bound | ||
|
|
||
| if upper_bound: | ||
| return node.val < upper_bound | ||
|
|
||
| return True |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| # 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: | ||
| return self.isValidBSTHelper(root, None, None) | ||
|
|
||
| def isValidBSTHelper(self, node: Optional[TreeNode], lower_bound: Optional[int], upper_bound: Optional[int]) -> bool: | ||
| if not node: | ||
| return True | ||
|
|
||
| if not self.isWithinIntervals(node, lower_bound, upper_bound): | ||
| return False | ||
|
|
||
| return self.isValidBSTHelper(node.left, lower_bound, node.val) and self.isValidBSTHelper(node.right, node.val, upper_bound) | ||
|
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. 自分なら if not self.isValidBSTHelper(node.left, lower_bound, node.val):
return False
if not self.isValidBSTHelper(node.right, node.val, upper_bound):
return False
return Trueとします。 |
||
|
|
||
| def isWithinIntervals(self, node: TreeNode, lower_bound: Optional[int], upper_bound: Optional[int]) -> bool: | ||
|
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. _isWithinIntervals, _isValidBSTHelperとしてプライベート関数として定義するといいと思いました
Owner
Author
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. ありがとうございます。是非そうさせていただきます🙇 |
||
| if lower_bound and upper_bound: | ||
|
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. 自分だったら最初に、lower_bound is None and upper_bound is None return Trueを書きます(特別な処理を先に書きたい) |
||
| return lower_bound < node.val < upper_bound | ||
|
|
||
| if lower_bound is not None: | ||
| return node.val > lower_bound | ||
|
|
||
| if upper_bound is not None: | ||
| return node.val < upper_bound | ||
|
|
||
| return True | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| class Solution: | ||
| def isValidBST(self, root: Optional[TreeNode]) -> bool: | ||
|
Owner
Author
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. 引数名がrootのままなのが少し嫌な気がする |
||
| if not root: | ||
| return True | ||
|
|
||
| left_max, _ = self.subtreeMinMax(root.left) | ||
| _, right_min = self.subtreeMinMax(root.right) | ||
|
|
||
| if left_max is not None and root.val <= left_max: | ||
| return False | ||
| if right_min is not None and root.val >= right_min: | ||
| return False | ||
|
|
||
| return self.isValidBST(root.left) and self.isValidBST(root.right) | ||
|
|
||
| @cache | ||
|
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. ここにキャッシュがついていると、上の isValidBST を2回呼んだ時、つまり、木を変更してもう一回呼んだときに、TreeNode の id でキャッシュされると思われるので、木の先のほうが変更されていても、同じ値が返るという意味でうまくいかないのではないでしょうか。 なので、isValidBST をヘルパー関数に変更して、インナーファンクションとして isValidBST と subtreeMinMax を書いたほうがいいのではないでしょうかね。 |
||
| def subtreeMinMax(self, node: Optional[TreeNode]) -> Tuple[Optional[int], Optional[int]]: | ||
| if not node: | ||
| return None, None | ||
|
|
||
| left_max, left_min = self.subtreeMinMax(node.left) | ||
| right_max, right_min = self.subtreeMinMax(node.right) | ||
|
|
||
| if left_max is not None and right_max is not None: | ||
| return max(node.val, left_max, right_max), min(node.val, left_min, right_min) | ||
| if left_max is not None: | ||
| return max(node.val, left_max), min(node.val, left_min) | ||
| if right_max is not None: | ||
| return max(node.val, right_max), min(node.val, right_min) | ||
|
|
||
| return node.val, node.val | ||
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.
infでいいのではないでしょうか?
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.
fhiyo/leetcode#41 (comment)
この辺の会話を思い出しており, 型がfloat型なのでmath.infを使いたくなかったというのが正直なところです。どのみちマジックナンバーであまりよくはないのですが...
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.
なるほどです。自分ならPythonは型の柔軟性が高いので気にせず使っちゃってコード量の削減を優先するかなと思いました。OptionalだとNoneの確認が面倒なので
好みの問題だとは思います。