Validate Binary Search Tree - JS Solution

JS
Editorial

Solution explanation

Solution explanation

Approach

We validate the binary search tree using a recursive helper validate(node, lower, upper) that enforces a range invariant for every node, where node.val must be greater than lower and less than upper. These bounds are inherited from ancestors and become tighter as we go down the tree, ensuring that each subtree also maintains the BST property internally.

🔧 Invariant & Recurrence

At each call validate(node, lower, upper):

  • Base case: if node == null, the path is valid since there’s nothing left to violate the rules along this path.
  • Bounds check: return false if node.val ≤ lower or node.val ≥ upper — the current node breaks the allowed range.
  • Recurse left: call validate(node.left, lower, node.val) to ensure all values in the left subtree are greater than the inherited lower bound and less than the current node’s value.
  • Recurse right: call validate(node.right, node.val, upper) to ensure all values in the right subtree are greater than the current node’s value and less than the inherited upper bound.
  • Return the result of the left recursion and the result of the right recursion. If both return true, it confirms that every node in both subtrees satisfies the inherited bounds from ancestors and maintains the BST property internally. Since the current node’s value was already validated before these calls, it means the entire subtree rooted here also satisfies the inherited bounds from ancestors and maintains the BST property internally. Otherwise, a violation was found in one of the recursive branches.

💡 Why the Bounds Are Sufficient

When we make recursive calls to the left and right subtrees, we’ve already confirmed that the current node’s value lies strictly within (lower, upper). For the left subtree, every value must be less than the current node’s value, so we tighten the upper bound to node.val. There’s no need to compare node.val with the inherited upper bound again — we already ensured that node.val < upper. Similarly, for the right subtree, every value must be greater than the current node’s value, so we tighten the lower bound to node.val. Again, there’s no need to compare node.val with the inherited lower bound — we already confirmed that node.val > lower.

⏱️ Complexity

Time: O(n) — each node is visited once.
Space: O(h) auxiliary — recursion stack height, where h is the tree height (worst case O(n) for a skewed tree; O(log n) for a balanced tree).

🧭 Result Semantics

If a call to validate(node, lower, upper) returns true, then the entire subtree rooted at node satisfies both the BST property internally and all value constraints inherited from its ancestors.

Time Complexity

O(n), where n is the number of nodes — each node is visited once.

Space Complexity

Auxiliary space usage is O(h) — the recursion stack height, where h is the tree height (worst case: O(n) for a skewed tree; average case: O(log n) for a balanced tree).

Visucodize editorial solution titled JS Solution for LeetCode's Validate Binary Search Tree coding problem. Includes code and may include explanation. You can animate the code step by step using the provided input by clicking the run button, or fork it locally to update the code and input for custom visualization. View the original problem at https://leetcode.com/problems/validate-binary-search-tree.