The problem. Given a binary search tree and two of its nodes p and q, return their lowest common ancestor — the deepest node that has both p and q in its subtree. A node counts as its own ancestor.
root = [6, 2, 8, 0, 4, 7, 9, null, null, 3, 5]
p = 2, q = 8 -> 6
p = 2, q = 4 -> 2 (a node is its own ancestor)
p = 3, q = 5 -> 4
p = 7, q = 9 -> 8In a general binary tree you'd have to search both subtrees. In a BST the values tell you where p and q are. If both are smaller than the current node, both live in the left subtree — so does their LCA. If both are bigger, go right. The first node where they split (one on each side, or one of them is the node) is the answer.
The classic binary-tree solution works on a BST too: ask each subtree whether it contains p or q, and the node where both sides answer "yes" is the LCA.
function lowestCommonAncestor(root, p, q) { if (root === null || root === p || root === q) return root; const left = lowestCommonAncestor(root.left, p, q); const right = lowestCommonAncestor(root.right, p, q); if (left !== null && right !== null) return root; // one on each side return left !== null ? left : right;}Time O(n), space O(h). It may visit every node — it never uses the ordering.
function lowestCommonAncestor(root, p, q) { if (p.val < root.val && q.val < root.val) return lowestCommonAncestor(root.left, p, q); if (p.val > root.val && q.val > root.val) return lowestCommonAncestor(root.right, p, q); return root; // they split here (or one of them is root)}Time O(h), space O(h) for the call stack — one call per level on a single path.
Both smaller: go left. Both bigger: go right. Otherwise this node is where they split.
function lowestCommonAncestor(root, p, q) { let node = root; while (node !== null) { if (p.val < node.val && q.val < node.val) node = node.left; // both on the left else if (p.val > node.val && q.val > node.val) node = node.right; // both on the right else return node; // they split here } return null;}Time O(h), space O(1).