Skip to content
DSAPrep

    ↑↓ to move · Enter to open · Esc to close

    MediumTrees

    Lowest Common Ancestor of a Binary Search Tree

    Given a binary search tree (BST), find the lowest common ancestor (LCA) node of two given nodes in the BST.

    According to the definition of LCA on Wikipedia, the lowest common ancestor is defined between two nodes p and q as the lowest node in the tree that has both p and q as descendants (where we allow a node to be a descendant of itself).

    Example 1

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

    Output: 6

    Explanation: The LCA of nodes 2 and 8 is 6.

    Example 2

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

    Output: 2

    Explanation: The LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition.

    Constraints

    • The number of nodes in the tree is in the range [2, 10^5].
    • -10^9 <= Node.val <= 10^9
    • All Node.val are unique.
    • p != q
    • p and q will exist in the BST.
    View original on LeetCode ↗

    A general binary tree needs a full traversal to find an LCA, but a BST’s ordering lets it be found by comparing values alone: at every node, both p and q being on the same side tells you which way to go, and them straddling the current node (or one equalling it) means you have arrived.

    Recursive (BST property)

    Time O(h)Space O(h)

    If both target values are less than the current node, the LCA must be in the left subtree. If both are greater, it must be in the right subtree. Otherwise the current node is exactly where the two paths diverge (or one target is the current node) — that is the LCA.

    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 p.val < root.val and q.val < root.val:
    return self.lowestCommonAncestor(root.left, p, q)
    if p.val > root.val and q.val > root.val:
    return self.lowestCommonAncestor(root.right, p, q)
    return root

    Complexity: the walk follows a single downward path from root to the LCA, never branching → O(h) time, where h is the tree height (O(log n) for a balanced BST, O(n) skewed). Recursion depth matches → O(h) space.

    Iterative

    OptimalTime O(h)Space O(1)

    The recursion above is tail recursion — at each step it just moves to a child and repeats. That translates directly into a loop with no extra stack frames.

    class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
    node = root
    while node:
    if p.val < node.val and q.val < node.val:
    node = node.left
    elif p.val > node.val and q.val > node.val:
    node = node.right
    else:
    return node

    Watch the walk descend and prune at each step (trace data lives in this problem folder’s data.ts):

    6
    2
    8
    0
    4
    7
    9
    3
    5

    on path: 6

    Decision at node 6

    p = 3q = 5
    both below?YY
    both above?nn
    p and q split?Yn
    1 / 6
    comparingseenresultcurrentdiscarded

    Start at the root, node 6. The loop holds a current node and compares p = 3 and q = 5 against its value to pick which side to descend.

    Why prefer this: identical O(h) time, but no call stack is used at all → O(1) space, an improvement over the recursive version’s O(h) stack, which matters on a very deep, unbalanced BST.

    More Trees practice