Given the root
of a binary tree, determine if it is a valid binary search tree (BST).
A valid BST is defined as follows:
For example, consider the following trees:
Example 1:
2
/ \
1 3
Input: root = [2,1,3]
Output: true
Example 2:
5
/ \
1 4
/ \
3 6
Input: root = [5,1,4,null,null,3,6]
Output: false
Explanation: The root node's value is 5 but its right child's value is 4.
Write a function to efficiently determine if a given binary tree is a valid BST. Explain the time and space complexity of your solution. Consider edge cases such as empty trees or trees with duplicate values. Can you provide both a naive and an optimal solution?
A straightforward, though not the most efficient, way to approach this problem is to perform an in-order traversal of the binary tree. During this traversal, we store the node values in an array. After the traversal, we check if the array is sorted in ascending order. If it is, the tree is a valid BST; otherwise, it is not.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def isValidBST_naive(root: TreeNode) -> bool:
inorder_values = []
def inorder_traversal(node):
if not node:
return
inorder_traversal(node.left)
inorder_values.append(node.val)
inorder_traversal(node.right)
inorder_traversal(root)
for i in range(1, len(inorder_values)):
if inorder_values[i] <= inorder_values[i - 1]:
return False
return True
The optimal approach avoids using extra space by checking the BST property during the in-order traversal itself. We keep track of the value of the previously visited node and compare it with the current node's value. If the current node's value is less than or equal to the previous node's value, the tree is not a valid BST.
previous
variable to None
.previous
value. If it is not, the tree is not a valid BST.previous
value to the current node's value.class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def isValidBST(root: TreeNode) -> bool:
previous = None
def inorder_traversal(node):
nonlocal previous
if not node:
return True
if not inorder_traversal(node.left):
return False
if previous is not None and node.val <= previous:
return False
previous = node.val
return inorder_traversal(node.right)
return inorder_traversal(root)
An alternative, and perhaps more clear, implementation utilizes a helper function that recursively validates the BST property for each node. The helper function receives a node, a minimum bound, and a maximum bound. The node's value must fall between these bounds (exclusive) for the subtree to be valid.
def isValidBST(root: TreeNode) -> bool:
def validate(node, low=-float('inf'), high=float('inf')):
if not node:
return True
if node.val <= low or node.val >= high:
return False
return (validate(node.left, low, node.val) and
validate(node.right, node.val, high))
return validate(root)
The optimal approach using in-order traversal with value comparison provides an efficient solution to determine if a binary tree is a valid BST, with a time complexity of O(N) and space complexity of O(H), where H is the height of the tree.