From e3856f900641c65a2058a24911df9559bd47fb9f Mon Sep 17 00:00:00 2001 From: SuperHotDogCat Date: Sat, 21 Dec 2024 21:02:46 +0900 Subject: [PATCH 1/8] solved Validate Binary Search Tree --- validate_binary_tree/phase1.py | 47 ++++++++++++++++++++++++ validate_binary_tree/phase2.py | 66 ++++++++++++++++++++++++++++++++++ validate_binary_tree/phase3.py | 38 ++++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 validate_binary_tree/phase1.py create mode 100644 validate_binary_tree/phase2.py create mode 100644 validate_binary_tree/phase3.py diff --git a/validate_binary_tree/phase1.py b/validate_binary_tree/phase1.py new file mode 100644 index 0000000..70f51ce --- /dev/null +++ b/validate_binary_tree/phase1.py @@ -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 + return self.isValidHelper(root, MIN, MAX) + + def isValidHelper(self, node: Optional[TreeNode], left, right) -> bool: + # left, rightは子に対する制約 left < node.child.val < rightであることを課す + # left, rightは親の値によってきまる + + if node.left and node.right: + 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 diff --git a/validate_binary_tree/phase2.py b/validate_binary_tree/phase2.py new file mode 100644 index 0000000..2806b2b --- /dev/null +++ b/validate_binary_tree/phase2.py @@ -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文の左側にもってくるなどの修正を加えるなど + +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 \ No newline at end of file diff --git a/validate_binary_tree/phase3.py b/validate_binary_tree/phase3.py new file mode 100644 index 0000000..d2453d3 --- /dev/null +++ b/validate_binary_tree/phase3.py @@ -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 From fa0fa079f502522d784e97c04797080d3eaa3741 Mon Sep 17 00:00:00 2001 From: SuperHotDogCat Date: Sat, 21 Dec 2024 21:06:40 +0900 Subject: [PATCH 2/8] =?UTF-8?q?phase2=E3=81=AE=E6=9C=AB=E5=B0=BE=E7=A9=BA?= =?UTF-8?q?=E8=A1=8C=E3=81=8C=E6=8A=9C=E3=81=91=E3=81=A6=E3=81=84=E3=81=9F?= =?UTF-8?q?=E3=81=AE=E3=81=A7=E7=9B=B4=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- validate_binary_tree/phase2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validate_binary_tree/phase2.py b/validate_binary_tree/phase2.py index 2806b2b..9d1f9f9 100644 --- a/validate_binary_tree/phase2.py +++ b/validate_binary_tree/phase2.py @@ -63,4 +63,4 @@ def generate_node_inorder(node): if prev_val is not None and prev_val >= node.val: return False prev_val = node.val - return True \ No newline at end of file + return True From 69fa435db8e2cd57d18ebeda83dad66edc456a18 Mon Sep 17 00:00:00 2001 From: SuperHotDogCat Date: Mon, 23 Dec 2024 15:16:40 +0900 Subject: [PATCH 3/8] =?UTF-8?q?=E5=AD=90=E3=81=AE=E5=80=A4=E3=82=92?= =?UTF-8?q?=E8=A6=AA=E3=81=8C=E7=A2=BA=E8=AA=8D=E3=81=97=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- validate_binary_tree/phase4.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 validate_binary_tree/phase4.py diff --git a/validate_binary_tree/phase4.py b/validate_binary_tree/phase4.py new file mode 100644 index 0000000..d8b39b8 --- /dev/null +++ b/validate_binary_tree/phase4.py @@ -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[TreeNode], upper_bound: Optional[TreeNode]) -> 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) + + 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 is not None: + return node.val > lower_bound + + if upper_bound is not None: + return node.val < upper_bound + + return True From e585107f76a4a29a2d42eca68ae3a91ede50480a Mon Sep 17 00:00:00 2001 From: SuperHotDogCat Date: Mon, 23 Dec 2024 16:08:54 +0900 Subject: [PATCH 4/8] modified phase4 typing --- validate_binary_tree/phase4.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validate_binary_tree/phase4.py b/validate_binary_tree/phase4.py index d8b39b8..aac6203 100644 --- a/validate_binary_tree/phase4.py +++ b/validate_binary_tree/phase4.py @@ -8,7 +8,7 @@ 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: + def isValidBSTHelper(self, node: Optional[TreeNode], lower_bound: Optional[int], upper_bound: Optional[int]) -> bool: if not node: return True @@ -17,7 +17,7 @@ def isValidBSTHelper(self, node: Optional[TreeNode], lower_bound: Optional[TreeN return self.isValidBSTHelper(node.left, lower_bound, node.val) and self.isValidBSTHelper(node.right, node.val, upper_bound) - def isWithinIntervals(self, node: TreeNode, lower_bound: Optional[TreeNode], upper_bound: Optional[TreeNode]) -> bool: + def isWithinIntervals(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 From 06596cdf4f7281c6431bcd6de697215435e1f9f0 Mon Sep 17 00:00:00 2001 From: SuperHotDogCat Date: Tue, 24 Dec 2024 10:39:59 +0900 Subject: [PATCH 5/8] =?UTF-8?q?=E4=B8=8B=E3=81=8B=E3=82=89=E3=81=AE?= =?UTF-8?q?=E5=86=8D=E5=B8=B0=E3=81=A7=E8=A7=A3=E3=81=84=E3=81=9F=E3=80=82?= =?UTF-8?q?math.inf=E3=82=92=E5=B0=91=E3=81=97=E6=94=B9=E5=96=84=E3=81=97?= =?UTF-8?q?=E3=81=9F=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- validate_binary_tree/phase5.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 validate_binary_tree/phase5.py diff --git a/validate_binary_tree/phase5.py b/validate_binary_tree/phase5.py new file mode 100644 index 0000000..21135c8 --- /dev/null +++ b/validate_binary_tree/phase5.py @@ -0,0 +1,23 @@ +class Solution: + def isValidBST(self, root: Optional[TreeNode]) -> bool: + if not root: + return True + + left_max, _ = self.subtreeMinMax(root.left) + _, right_min = self.subtreeMinMax(root.right) + + if not (left_max < root.val < right_min): + return False + + return self.isValidBST(root.left) and self.isValidBST(root.right) + + @cache + def subtreeMinMax(self, node: Optional[TreeNode]) -> Union[float, int]: + # nodeを含む部分木のmin, maxを出力する + if not node: + return -math.inf, math.inf + + left_max, left_min = self.subtreeMinMax(node.left) + right_max, right_min = self.subtreeMinMax(node.right) + + return max(node.val, left_max, right_max), min(node.val, left_min, right_min) From f92fb213f4338fd9ef6fe9e748aec7c245580d38 Mon Sep 17 00:00:00 2001 From: SuperHotDogCat Date: Tue, 24 Dec 2024 10:52:50 +0900 Subject: [PATCH 6/8] =?UTF-8?q?=E4=B8=8B=E3=81=8B=E3=82=89=E3=81=AE?= =?UTF-8?q?=E5=86=8D=E5=B8=B0=E3=81=A7=E8=A7=A3=E3=81=84=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- validate_binary_tree/phase5.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/validate_binary_tree/phase5.py b/validate_binary_tree/phase5.py index 21135c8..3cc86d1 100644 --- a/validate_binary_tree/phase5.py +++ b/validate_binary_tree/phase5.py @@ -3,21 +3,29 @@ def isValidBST(self, root: Optional[TreeNode]) -> bool: if not root: return True - left_max, _ = self.subtreeMinMax(root.left) - _, right_min = self.subtreeMinMax(root.right) + left_max, left_min = self.subtreeMinMax(root.left) + right_max, right_min = self.subtreeMinMax(root.right) - if not (left_max < root.val < right_min): + if left_max is not None and left_max >= root.val: return False - + if right_min is not None and right_min <= root.val: + return False + return self.isValidBST(root.left) and self.isValidBST(root.right) @cache - def subtreeMinMax(self, node: Optional[TreeNode]) -> Union[float, int]: - # nodeを含む部分木のmin, maxを出力する + def subtreeMinMax(self, node: Optional[TreeNode]) -> Tuple[Optional[int], Optional[int]]: if not node: - return -math.inf, math.inf + return None, None left_max, left_min = self.subtreeMinMax(node.left) right_max, right_min = self.subtreeMinMax(node.right) - return max(node.val, left_max, right_max), min(node.val, left_min, right_min) + 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 From d2f7cfa20c20b8c778f5ae6c3826419eee0057ed Mon Sep 17 00:00:00 2001 From: SuperHotDogCat Date: Tue, 24 Dec 2024 10:53:22 +0900 Subject: [PATCH 7/8] =?UTF-8?q?=E4=B8=8B=E3=81=8B=E3=82=89=E3=81=AE?= =?UTF-8?q?=E5=86=8D=E5=B8=B0=E3=81=A7=E8=A7=A3=E3=81=84=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- validate_binary_tree/phase5.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validate_binary_tree/phase5.py b/validate_binary_tree/phase5.py index 3cc86d1..41121c4 100644 --- a/validate_binary_tree/phase5.py +++ b/validate_binary_tree/phase5.py @@ -3,8 +3,8 @@ def isValidBST(self, root: Optional[TreeNode]) -> bool: if not root: return True - left_max, left_min = self.subtreeMinMax(root.left) - right_max, right_min = self.subtreeMinMax(root.right) + left_max, _ = self.subtreeMinMax(root.left) + _, right_min = self.subtreeMinMax(root.right) if left_max is not None and left_max >= root.val: return False From 04f9173e804f79a7ce66f079e177d4bbd0d6f0b7 Mon Sep 17 00:00:00 2001 From: SuperHotDogCat Date: Tue, 24 Dec 2024 10:56:05 +0900 Subject: [PATCH 8/8] =?UTF-8?q?=E4=B8=8D=E5=BD=93=E5=BC=8F=E3=82=92root.va?= =?UTF-8?q?l=E3=82=92=E5=B7=A6=E5=81=B4=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- validate_binary_tree/phase5.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validate_binary_tree/phase5.py b/validate_binary_tree/phase5.py index 41121c4..6fd433b 100644 --- a/validate_binary_tree/phase5.py +++ b/validate_binary_tree/phase5.py @@ -6,9 +6,9 @@ def isValidBST(self, root: Optional[TreeNode]) -> bool: left_max, _ = self.subtreeMinMax(root.left) _, right_min = self.subtreeMinMax(root.right) - if left_max is not None and left_max >= root.val: + if left_max is not None and root.val <= left_max: return False - if right_min is not None and right_min <= root.val: + if right_min is not None and root.val >= right_min: return False return self.isValidBST(root.left) and self.isValidBST(root.right)