diff --git a/data_structures/binary_tree/is_bst.py b/data_structures/binary_tree/is_bst.py new file mode 100644 index 000000000000..0b2ef8c9ffde --- /dev/null +++ b/data_structures/binary_tree/is_bst.py @@ -0,0 +1,131 @@ +""" +Author : Alexander Pantyukhin +Date : November 2, 2022 + +Task: +Given the root of a binary tree, determine if it is a valid binary search +tree (BST). + +A valid binary search tree is defined as follows: + +- The left subtree of a node contains only nodes with keys less than the node's key. +- The right subtree of a node contains only nodes with keys greater than the node's key. +- Both the left and right subtrees must also be binary search trees. + +Implementation notes: +Depth-first search approach. + +leetcode: https://leetcode.com/problems/validate-binary-search-tree/ + +Let n is the number of nodes in tree +Runtime: O(n) +Space: O(1) +""" + +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass +class TreeNode: + data: float + left: TreeNode | None = None + right: TreeNode | None = None + + +def is_binary_search_tree(root: TreeNode | None) -> bool: + """ + >>> is_binary_search_tree(TreeNode(data=2, + ... left=TreeNode(data=1), + ... right=TreeNode(data=3)) + ... ) + True + + >>> is_binary_search_tree(TreeNode(data=0, + ... left=TreeNode(data=-11), + ... right=TreeNode(data=3)) + ... ) + True + + >>> is_binary_search_tree(TreeNode(data=5, + ... left=TreeNode(data=1), + ... right=TreeNode(data=4, left=TreeNode(data=3))) + ... ) + False + + >>> is_binary_search_tree(TreeNode(data='a', + ... left=TreeNode(data=1), + ... right=TreeNode(data=4, left=TreeNode(data=3))) + ... ) + Traceback (most recent call last): + ... + ValueError: Each node should be type of TreeNode and data should be float. + + >>> is_binary_search_tree(TreeNode(data=2, + ... left=TreeNode([]), + ... right=TreeNode(data=4, left=TreeNode(data=3))) + ... ) + Traceback (most recent call last): + ... + ValueError: Each node should be type of TreeNode and data should be float. + """ + + # Validation + def is_valid_tree(node: TreeNode | None) -> bool: + """ + >>> is_valid_tree(None) + True + >>> is_valid_tree('abc') + False + >>> is_valid_tree(TreeNode(data='not a float')) + False + >>> is_valid_tree(TreeNode(data=1, left=TreeNode('123'))) + False + """ + if node is None: + return True + + if not isinstance(node, TreeNode): + return False + + try: + float(node.data) + except (TypeError, ValueError): + return False + + return is_valid_tree(node.left) and is_valid_tree(node.right) + + if not is_valid_tree(root): + raise ValueError( + "Each node should be type of TreeNode and data should be float." + ) + + def is_binary_search_tree_recursive_check( + node: TreeNode | None, left_bound: float, right_bound: float + ) -> bool: + """ + >>> is_binary_search_tree_recursive_check(None) + True + >>> is_binary_search_tree_recursive_check(TreeNode(data=1), 10, 20) + False + """ + + if node is None: + return True + + return ( + left_bound < node.data < right_bound + and is_binary_search_tree_recursive_check(node.left, left_bound, node.data) + and is_binary_search_tree_recursive_check( + node.right, node.data, right_bound + ) + ) + + return is_binary_search_tree_recursive_check(root, -float("inf"), float("inf")) + + +if __name__ == "__main__": + import doctest + + doctest.testmod()