235. Lowest Common Ancestor of a Binary Search Tree - Explanation

Problem Link

Description

Given a binary search tree (BST) where all node values are unique, and two nodes from the tree p and q, return the lowest common ancestor (LCA) of the two nodes.

The lowest common ancestor between two nodes p and q is the lowest node in a tree T such that both p and q as descendants. The ancestor is allowed to be a descendant of itself.

Example 1:

Input: root = [5,3,8,1,4,7,9,null,2], p = 3, q = 8

Output: 5

Example 2:

Input: root = [5,3,8,1,4,7,9,null,2], p = 3, q = 4

Output: 3

Explanation: The LCA of nodes 3 and 4 is 3, since a node can be a descendant of itself.

Constraints:

  • 2 <= The number of nodes in the tree <= 100.
  • -100 <= Node.val <= 100
  • p != q
  • p and q will both exist in the BST.


Topics

Recommended Time & Space Complexity

You should aim for a solution as good or better than O(h) time and O(h) space, where h is the height of the given tree.


Hint 1

A Binary Search Tree (BST) is a tree in which the values of all nodes in the left subtree of a node are less than the node's value, and the values of all nodes in the right subtree are greater than the node's value. Additionally, every subtree of a BST must also satisfy this property, meaning the "less than" or "greater than" condition is valid for all nodes in the tree, not just the root. How can you use this idea to find the LCA of the given nodes in the tree?


Hint 2

We can use recursion to traverse the tree. Can you figure out the conditions we encounter when choosing a path between the left and right subtrees during traversal using the values of the two given nodes? Perhaps you can determine the LCA by traversing based on these conditions.


Hint 3

If nodes p and q are in different subtrees, a split occurs, making the current node the LCA. If both are in the left or right subtree, the LCA lies in that subtree and we further choose that subtree to traverse using recursion. You should also handle other multiple scenarios to get the LCA.


Hint 4

The LCA can also be one of the nodes, p or q, if the current node is equal to either of them. This is because if we encounter either p or q during the traversal, that node is the LCA.


Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Binary Search Tree Properties - Understanding that left subtree values are smaller and right subtree values are larger than the node's value
  • Tree Traversal - Ability to navigate through a tree using recursion or iteration
  • Recursion (optional) - One approach uses recursive calls to traverse down the tree until the split point is found

1. Recursion

Intuition

We are working with a Binary Search Tree (BST), so:

  • All values in the left subtree of a node are smaller than the node’s value.
  • All values in the right subtree are greater than the node’s value.

For two nodes p and q:

  • If both values are smaller than the current node -> both must lie in the left subtree.
  • If both values are greater than the current node -> both must lie in the right subtree.
  • Otherwise, the current node is the split point where one node is on the left and the other is on the right (or one is equal to the current node).
    That split point is the Lowest Common Ancestor (LCA).

Algorithm

  1. Start from the root.
  2. While at a node:
    • If both p and q have values less than the current node's value:
      • Move to the left child.
    • Else if both p and q have values greater than the current node's value:
      • Move to the right child.
    • Otherwise:
      • The current node is the LCA -> return it.
  3. If the tree is empty, return null.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        if not root or not p or not q:
            return None
        if (max(p.val, q.val) < root.val):
            return self.lowestCommonAncestor(root.left, p, q)
        elif (min(p.val, q.val) > root.val):
            return self.lowestCommonAncestor(root.right, p, q)
        else:
            return root

Time & Space Complexity

  • Time complexity: O(h)O(h)
  • Space complexity: O(h)O(h)

Where hh is the height of the tree.


2. Iteration

Intuition

This is the iterative version of finding the Lowest Common Ancestor (LCA) in a Binary Search Tree (BST).
Because a BST is ordered:

  • Left subtree < node < right subtree

We can decide where both nodes lie just by comparing values.

  • If p and q are both greater than the current node -> move right.
  • If they are both smaller -> move left.
  • If they split (one on each side) or one equals the current node ->
    current node is the LCA, because it's the first node where their paths diverge.

This avoids recursion and simply walks down the tree until the split point is found.

Algorithm

  1. Set cur = root.
  2. While cur is not null:
    • If p.val and q.val are both greater than cur.val -> go right.
    • Else if both are smaller -> go left.
    • Otherwise:
      • You've found the first node where their paths separate -> return cur (the LCA).
  3. Return null if tree is empty (should not happen for valid input).
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        cur = root

        while cur:
            if p.val > cur.val and q.val > cur.val:
                cur = cur.right
            elif p.val < cur.val and q.val < cur.val:
                cur = cur.left
            else:
                return cur

Time & Space Complexity

  • Time complexity: O(h)O(h)
  • Space complexity: O(1)O(1)

Where hh is the height of the tree.


Common Pitfalls

Ignoring BST Properties

A common mistake is treating this problem like a general binary tree LCA problem. In a BST, values are ordered (left < node < right), which allows us to determine the direction to search based on value comparisons alone. Using a generic DFS approach that checks both subtrees wastes time and ignores the BST structure that makes O(h) solutions possible.

Incorrect Comparison Logic

When comparing p and q values against the current node, be careful with the boundary conditions. The LCA is found when the current node's value lies between p.val and q.val (inclusive). A common bug is using strict inequalities everywhere, which fails when p or q equals the current node. Remember: if p.val <= root.val <= q.val (or vice versa), the current node is the LCA.