Talentd
FleetCode
Make and follow sheets.Sign in

Lowest Common Ancestor of a Binary Search Tree solution and explanation

MediumBinary Search TreeBinary TreeDepth-First SearchTree7 min read71.4% acceptanceUpdated 9 Apr 2026
Lowest Common Ancestor of a Binary Search Tree - Leetcode 235 - PythonNeetCode · 6:43 · 363K viewsWatch on YouTube
9 more videos on this problem
  1. Lowest Common Ancestor of a Binary Search Tree - Leetcode 235 - PythonNeetCode · 6:43 · 363K views
  2. LeetCode 235. Lowest Common Ancestor of a Binary Search Tree (Algorithm Explained)Nick White · 6:36 · 28K views
  3. Lowest Common Ancestor of a Binary Search Tree - Leetcode 235 - Trees (Python)Greg Hogg · 8:48 · 14K views
  4. Lowest Common Ancestor of a Binary Search Tree - LeetCode 235 - PythonDeepti Talesra · 10:54 · 5K views
  5. [Java] Leetcode 235. Lowest Common Ancestor of a Binary Search Tree [Binary Search Tree #4]Eric Programming · 4:34 · 3K views
  6. LeetCode 235. Lowest Common Ancestor of a Binary Search Tree - Interview Prep Ep 137Fisher Coder · 10:24 · 785 views
  7. LCA Of A BST | LeetCode 235 (Easy)Destination FAANG · 10:48 · 771 views
  8. Lowest Common Ancestor of a Binary Search Tree (Java) | Leetcode 235 |Puru Arora · 8:15 · 397 views
  9. Lowest Common Ancestor of a Binary Search Tree - 235. LeetCode - JavaTeddy Smith · 7:58 · 372 views
  10. LeetCode Problem 235: Lowest Common Ancestor of a Binary Search Tree || FAANG Interview || Blind 75KeyStrokes · 11:43 · 320 views

The problem

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 T 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.

Example 3:

Input: root = [2,1], p = 2, q = 1
Output: 2

Constraints:

  • The number of nodes in the tree is in the range [2, 105].
  • -109 <= Node.val <= 109
  • All Node.val are unique.
  • p != q
  • p and q will exist in the BST.

Approach overview

Problem Overview: Given a Binary Search Tree (BST) and two nodes p and q, return their lowest common ancestor (LCA). The LCA is the lowest node in the tree that has both nodes as descendants. Because the structure is a BST, every node follows the rule left < root < right, which allows you to locate the ancestor without scanning the entire tree.

Approach 1: Recursive BST Traversal (O(h) time, O(h) space)

The BST property gives a direct way to locate the ancestor. Start from the root and compare the values of p and q with the current node. If both values are smaller than the current node, the LCA must be in the left subtree. If both are larger, move to the right subtree. The first node where the values split (one on each side) is the lowest common ancestor. The recursion follows a single path from the root down the tree, so the time complexity is O(h), where h is the tree height, and recursion adds O(h) call stack space.

This approach naturally mirrors how you search in a Binary Search Tree. It is concise and easy to reason about during interviews because each recursive step eliminates half of the search space.

Approach 2: Iterative Traversal (O(h) time, O(1) space)

The iterative version applies the exact same BST insight but replaces recursion with a loop. Start from the root and repeatedly compare p.val and q.val with the current node value. If both are smaller, move to node.left. If both are larger, move to node.right. When the current node lies between the two values (or equals one of them), you have found the LCA.

This method walks down only one branch of the tree, so the time complexity remains O(h). Because it uses a simple pointer traversal instead of recursive calls, the space complexity improves to O(1). In balanced BSTs the height is O(log n), but in the worst case of a skewed tree it becomes O(n).

The approach relies heavily on the ordering guarantee of a tree that satisfies BST rules. Unlike general Depth-First Search LCA problems, you never need to explore both subtrees.

Recommended for interviews: The iterative BST traversal is usually the expected optimal solution because it runs in O(h) time with constant space. The recursive version still demonstrates a solid understanding of BST structure and is often written faster during interviews. Strong candidates quickly recognize that the BST ordering eliminates the need for full DFS or path tracking.

Approach 1: Recursive Approach

This method exploits the properties of a BST. The idea is to traverse the tree starting from the root. If both p and q are greater than the current node, then the LCA lies in the right subtree. If both are smaller, then it lies in the left subtree. Otherwise, the current node is the LCA.

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if not root:
            return None

        if p.val > root.val and q.val > root.val:
            return self.lowestCommonAncestor(root.right, p, q)
        elif p.val < root.val and q.val < root.val:
            return self.lowestCommonAncestor(root.left, p, q)
        else:
            return root
public class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) return null;
        if (p.val > root.val && q.val > root.val)
            return lowestCommonAncestor(root.right, p, q);
        else if (p.val < root.val && q.val < root.val)
            return lowestCommonAncestor(root.left, p, q);
        else
            return root;
    }
}
#include <iostream>
using namespace std;

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (!root) return nullptr;
        if (p->val > root->val && q->val > root->val)
            return lowestCommonAncestor(root->right, p, q);
        else if (p->val < root->val && q->val < root->val)
            return lowestCommonAncestor(root->left, p, q);
        else
            return root;
    }
};
function TreeNode(val) {
    this.val = val;
    this.left = this.right = null;
}

var lowestCommonAncestor = function(root, p, q) {
    if (!root) return null;
    if (p.val > root.val && q.val > root.val)
        return lowestCommonAncestor(root.right, p, q);
    else if (p.val < root.val && q.val < root.val)
        return lowestCommonAncestor(root.left, p, q);
    else
        return root;
};
#include <stdio.h>

struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};

struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
    if (root == NULL) return NULL;
    if (p->val > root->val && q->val > root->val) {
        return lowestCommonAncestor(root->right, p, q);
    } else if (p->val < root->val && q->val < root->val) {
        return lowestCommonAncestor(root->left, p, q);
    } else {
        return root;
    }
}
public class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;
    public TreeNode(int x) { val = x; }
}

public class Solution {
    public TreeNode LowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) return null;
        if (p.val > root.val && q.val > root.val)
            return LowestCommonAncestor(root.right, p, q);
        else if (p.val < root.val && q.val < root.val)
            return LowestCommonAncestor(root.left, p, q);
        else
            return root;
    }
}

This Python function uses recursion to navigate the tree, choosing branches based on node values. The LCA is found by determining when p and q reside on different sides of a node.

Complexity

Time Complexity: O(h), where h is the height of the tree.
Space Complexity: O(h) due to function recursion stack.

Try this approach in the editor

Approach 2: Iterative Approach

Instead of recursion, this method uses a while loop to traverse the tree. Starting from the root, check if both nodes are greater or smaller than the current node to decide whether to proceed to the right or left subtree respectively. If neither, we've found the LCA.

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        current = root
        while current:
            if p.val > current.val and q.val > current.val:
                current = current.right
            elif p.val < current.val and q.val < current.val:
                current = current.left
            else:
                return current
        return None
public class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        TreeNode current = root;
        while (current != null) {
            if (p.val > current.val && q.val > current.val) {
                current = current.right;
            } else if (p.val < current.val && q.val < current.val) {
                current = current.left;
            } else {
                return current;
            }
        }
        return null;
    }
}
#include <iostream>
using namespace std;

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        TreeNode* current = root;
        while (current != NULL) {
            if (p->val > current->val && q->val > current->val) {
                current = current->right;
            } else if (p->val < current->val && q->val < current->val) {
                current = current->left;
            } else {
                return current;
            }
        }
        return nullptr;
    }
};
function TreeNode(val) {
    this.val = val;
    this.left = this.right = null;
}

var lowestCommonAncestor = function(root, p, q) {
    let current = root;
    while (current) {
        if (p.val > current.val && q.val > current.val) {
            current = current.right;
        } else if (p.val < current.val && q.val < current.val) {
            current = current.left;
        } else {
            return current;
        }
    }
    return null;
};
#include <stdio.h>

struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};

struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
    struct TreeNode* current = root;
    while (current != NULL) {
        if (p->val > current->val && q->val > current->val) {
            current = current->right;
        } else if (p->val < current->val && q->val < current->val) {
            current = current->left;
        } else {
            return current;
        }
    }
    return NULL;
}
public class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;
    public TreeNode(int x) { val = x; }
}

public class Solution {
    public TreeNode LowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        TreeNode current = root;
        while (current != null) {
            if (p.val > current.val && q.val > current.val) {
                current = current.right;
            } else if (p.val < current.val && q.val < current.val) {
                current = current.left;
            } else {
                return current;
            }
        }
        return null;
    }
}

This Python code employs a loop to iteratively navigate the BST using comparisons to find the LCA, which ensures efficient space usage.

Complexity

Time Complexity: O(h), where h is the height of the tree.
Space Complexity: O(1) due to iterative nature with no extra space usage.

Try this approach in the editor

Approach 3: Iteration

Starting from the root node, we traverse the tree. If the current node's value is less than both p and q values, it means that p and q should be in the right subtree of the current node, so we move to the right child. If the current node's value is greater than both p and q values, it means that p and q should be in the left subtree, so we move to the left child. Otherwise, it means the current node is the lowest common ancestor of p and q, so we return the current node.

The time complexity is O(n), where n is the number of nodes in the binary search tree. The space complexity is O(1).

/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */

function lowestCommonAncestor(
    root: TreeNode | null,
    p: TreeNode | null,
    q: TreeNode | null,
): TreeNode | null {
    while (root) {
        if (root.val > p.val && root.val > q.val) {
            root = root.left;
        } else if (root.val < p.val && root.val < q.val) {
            root = root.right;
        } else {
            return root;
        }
    }
}
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val   int
 *     Left  *TreeNode
 *     Right *TreeNode
 * }
 */

func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
	for {
		if root.Val < min(p.Val, q.Val) {
			root = root.Right
		} else if root.Val > max(p.Val, q.Val) {
			root = root.Left
		} else {
			return root
		}
	}
}
Try this approach in the editor

Approach 4: Recursion

We can also use a recursive approach to solve this problem.

We first check if the current node's value is less than both p and q values. If it is, we recursively traverse the right subtree. If the current node's value is greater than both p and q values, we recursively traverse the left subtree. Otherwise, it means the current node is the lowest common ancestor of p and q, so we return the current node.

The time complexity is O(n), and the space complexity is O(n). Where n is the number of nodes in the binary search tree.

/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */

function lowestCommonAncestor(
    root: TreeNode | null,
    p: TreeNode | null,
    q: TreeNode | null,
): TreeNode | null {
    if (root.val > p.val && root.val > q.val) {
        return lowestCommonAncestor(root.left, p, q);
    }
    if (root.val < p.val && root.val < q.val) {
        return lowestCommonAncestor(root.right, p, q);
    }
    return root;
}
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val   int
 *     Left  *TreeNode
 *     Right *TreeNode
 * }
 */

func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
	if root.Val < p.Val && root.Val < q.Val {
		return lowestCommonAncestor(root.Right, p, q)
	}
	if root.Val > p.Val && root.Val > q.Val {
		return lowestCommonAncestor(root.Left, p, q)
	}
	return root
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Recursive Approach

Time Complexity: O(h), where h is the height of the tree.
Space Complexity: O(h) due to function recursion stack.

Iterative Approach

Time Complexity: O(h), where h is the height of the tree.
Space Complexity: O(1) due to iterative nature with no extra space usage.

Iteration–
Recursion–

Complexity analysis

ApproachTimeSpaceWhen to Use
Recursive BST TraversalO(h)O(h)When recursion is acceptable and you want concise logic that mirrors BST search
Iterative BST TraversalO(h)O(1)Preferred for interviews or memory‑constrained environments since it avoids recursion stack

Frequently asked questions

Is Lowest Common Ancestor of a Binary Search Tree easy or hard?

The problem is generally rated Medium because it requires recognizing and exploiting the Binary Search Tree property. Once that insight is clear, the implementation is straightforward and often fits in a few lines of code.

Lowest Common Ancestor of a Binary Search Tree Python/Java solution

Python and Java solutions typically implement either recursion or an iterative loop starting from the root. Both compare node values with p and q to decide whether to move left or right. The logic is identical across languages and runs in O(h) time.

How to solve Lowest Common Ancestor of a Binary Search Tree in O(n)?

A general tree approach performs a DFS traversal and returns the node where the paths to p and q diverge. That solution explores both subtrees and runs in O(n) time with O(h) recursion space. For BSTs specifically, the ordering property allows a faster O(h) solution by following a single branch from the root.

What is the best approach for Lowest Common Ancestor of a Binary Search Tree?

The optimal approach uses the Binary Search Tree ordering property while traversing from the root. If both nodes are smaller than the current node, move left; if both are larger, move right. The first node where the values diverge is the lowest common ancestor. This runs in O(h) time where h is the tree height and requires O(1) space in the iterative version.

Is Lowest Common Ancestor of a Binary Search Tree asked at Google/Amazon/Meta?

Lowest Common Ancestor problems are common interview questions at companies like Google, Amazon, Meta, and Microsoft. Variants appear with Binary Trees, Binary Search Trees, or parent pointers. The BST version is frequently used to test understanding of tree traversal and ordering properties.

What data structure is used in Lowest Common Ancestor of a Binary Search Tree?

The problem uses a Binary Search Tree data structure. The solution relies on the BST ordering rule where all nodes in the left subtree are smaller than the root and all nodes in the right subtree are larger, enabling efficient ancestor discovery.

What is the time complexity of Lowest Common Ancestor of a Binary Search Tree?

The standard BST-based solution runs in O(h) time, where h is the height of the tree. In a balanced BST this becomes O(log n). In the worst case of a skewed tree, the height becomes n and the complexity degrades to O(n).

Comments

Now write it yourselfClose this page and solve Lowest Common Ancestor of a Binary Search Tree in the editor, without looking.
Practise Lowest Common Ancestor of a Binary Search Tree