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..9d1f9f9 --- /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 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 diff --git a/validate_binary_tree/phase4.py b/validate_binary_tree/phase4.py new file mode 100644 index 0000000..aac6203 --- /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[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) + + 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 + + if lower_bound is not None: + return node.val > lower_bound + + if upper_bound is not None: + return node.val < upper_bound + + return True diff --git a/validate_binary_tree/phase5.py b/validate_binary_tree/phase5.py new file mode 100644 index 0000000..6fd433b --- /dev/null +++ b/validate_binary_tree/phase5.py @@ -0,0 +1,31 @@ +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 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 + 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