You are given the definition of a binary tree node:
class Node:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
Write a function is_bst(root)
that takes the root of a binary tree as input and returns True
if the tree is a valid Binary Search Tree (BST), and False
otherwise.
Definition of a BST:
Example 1:
Input: root = [2,1,3]
Output: true
2
/ \
1 3
Example 2:
Input: root = [5,1,4,null,null,3,6]
Output: false
5
/ \
1 4
/ \
3 6
Explanation: The root node's value is 5 but its right child's value is 4.
Constraints:
[1, 10000]
.-10000 <= Node.val <= 10000
Your solution should handle edge cases such as an empty tree or a tree with only one node.
class Node:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
# Naive Solution
# Check for each node if its value is greater than all values in the left subtree
# and smaller than all values in the right subtree
# This involves traversing the entire left and right subtrees for each node
def is_bst_naive(root):
if not root:
return True
def get_max(node):
if not node:
return float('-inf')
return max(node.val, get_max(node.left), get_max(node.right))
def get_min(node):
if not node:
return float('inf')
return min(node.val, get_min(node.left), get_min(node.right))
if (root.left and get_max(root.left) >= root.val) or (root.right and get_min(root.right) <= root.val):
return False
return is_bst_naive(root.left) and is_bst_naive(root.right)
# Optimal Solution
# Use the concept of range. For each node, check if its value is within the valid range
# The valid range is determined by the values of its ancestors
def is_bst(root):
def is_bst_helper(node, min_val, max_val):
if not node:
return True
if (min_val is not None and node.val <= min_val) or (max_val is not None and node.val >= max_val):
return False
return is_bst_helper(node.left, min_val, node.val) and is_bst_helper(node.right, node.val, max_val)
return is_bst_helper(root, None, None)
# Example Usage:
# Example 1
root1 = Node(2, Node(1), Node(3))
print(f"Is BST: {is_bst(root1)}") # Output: True
# Example 2
root2 = Node(5, Node(1), Node(4, Node(3), Node(6)))
print(f"Is BST: {is_bst(root2)}") # Output: False
# Example 3: Single node tree
root3 = Node(10)
print(f"Is BST: {is_bst(root3)}") # Output: True
# Example 4: Empty tree
root4 = None
print(f"Is BST: {is_bst(root4)}") # Output: True
# Example 5:
root5 = Node(5, Node(4), Node(6, Node(3), Node(7)))
print(f"Is BST: {is_bst(root5)}") # Output: False
is_bst
(Optimal Solution):is_bst
function uses a helper function is_bst_helper
which performs a depth-first traversal of the binary tree.min_val
and max_val
).is_bst
(Optimal Solution):is_bst_helper
.None
, the function should return True
because an empty tree is a valid BST.True
because a single node tree is a valid BST.min_val
and max_val
. Consider using a larger data type or a different approach if this is a concern.(min_val is not None and node.val <= min_val) or (max_val is not None and node.val >= max_val)
correctly handles cases where node.val
is equal to min_val
or max_val
. In a valid BST, all nodes in the left subtree should be strictly less than the current node and all nodes in the right subtree should be strictly greater than the current node.