How do you validate that a binary tree is a valid binary search tree?
Carry the range that each node is allowed to fall into. A node's value must be strictly greater than every value in its ancestors' left subtrees and strictly less than every value in their right subtrees, so as you descend you tighten the bounds and check each node against them.
class TreeNode:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
def is_valid_bst(root):
def check(node, low, high):
if node is None:
return True
if node.value <= low or node.value >= high:
return False
return check(node.left, low, node.value) and check(node.right, node.value, high)
return check(root, float('-inf'), float('inf'))
print(is_valid_bst(TreeNode(2, TreeNode(1), TreeNode(3))))
print(is_valid_bst(TreeNode(5, TreeNode(1, TreeNode(6)), TreeNode(4))))The outputs are True and False. The first tree has 1 on the left and 3 on the right of 2, which is correct. The second has a 4 in the right subtree of 5 and a 6 inside the left subtree, and both break the ordering.
The mechanism is that the bounds carry the constraints of all the ancestors at once. When you go left the upper bound becomes the current node's value, and when you go right the lower bound becomes it, so the check at each node automatically enforces every rule that applies to it.
The trade-off to name is the bounds approach uses strict comparisons, so duplicate values are rejected. Some definitions allow equal values on one side, and if you need that, one of the two comparisons becomes non-strict, but you have to decide which side is allowed to hold the duplicate and say so. The recursive version also uses stack space proportional to the height, so a skewed tree of a hundred thousand nodes would overflow, and an iterative version with an explicit stack of bounds would avoid that.