Start learning free

LESSON 472 OF 667 · BINARY TREES

Lowest Common Ancestor in a BST in C++: Iterative O(h) Walk

Practice O(n) Read the full explanation

You get the root of a binary search tree (BST). You also get two values, p and q, that are both in the tree. Return the value of their lowest common ancestor.

That is the deepest node with both p and q in its subtree. A node counts as part of its own subtree. So if p is above q, the answer is p.

In a BST, every value in a node's left subtree is smaller than the node's value. Every value in its right subtree is larger.

Trees are written level by level, from top to bottom and left to right. A NULL marks a missing child.

 

Example 1:

        8
       / \
      3   12
         /  \
       10    15
      /  \
     9    11

Input: root = [8,3,12,NULL,NULL,10,15,9,11], p = 9, q = 15
Output: 12
Explanation: 9 is in the left subtree of 12.
15 is in its right subtree.
No deeper node has both of them below it.

Example 2:

Input: root = [8,3,12,NULL,NULL,10,15,9,11], p = 11, q = 12
Output: 12
Explanation: 11 is below 12.
12 is part of its own subtree, so the answer is 12 itself.

Example 3:

Input: root = [0,-4,6], p = 6, q = 6
Output: 6
Explanation: p and q can be the same value.
Then the answer is that node.

 

Constraints:

  • 1 to 15 nodes.
  • Each value is a whole number from -100 to 100.
  • No two nodes have the same value.
  • p and q are values in the tree. They can be equal.

 

Follow up: Can you do it in O(h) time and O(1) extra space? Here h is the height of the tree.
STEP 1 OF 6
    Ctrl + Enter to check

    C++ · BINARY TREES

    Lca In Bst in C++, explained

    Problem overview

    The function lowestCommonAncestor receives the root of a binary search tree and two values, p and q, that are both in the tree. It returns the value of their lowest common ancestor: the deepest node that has both values in its subtree. A node counts as its own ancestor, and p and q may be equal.

    In the tree with root 6, children 2 and 8, and 2 having children 0 and 4 (4 having children 3 and 5), the answers are:

    • p = 2, q = 8 gives 6: they sit on different sides of the root.
    • p = 2, q = 4 gives 2: 4 is inside the subtree of 2, and a node is its own ancestor.
    • p = 5, q = 3 gives 4: they are the two children of 4.

    The follow-up asks for O(h) time, where h is the height of the tree, and O(1) extra space. This lesson does exactly that.

    Approach: let the values choose the direction

    For a general binary tree you would have to search for both nodes. A BST gives more: everything smaller than a node's value is in its left subtree, and everything larger is in its right subtree. Start at the root, which is above everything and so is a common ancestor, and look for the lowest one:

    • If both p and q are smaller than the current value, they are both in the left subtree, and so is their lowest common ancestor. Go left.
    • If both are larger, go right.
    • Otherwise they are split: one is on each side, or one of them equals the current value. Going down either way would leave a value behind, so this node is the answer.

    Take p = 5, q = 3. At 6 both are smaller, so go to 2. At 2 both are larger, so go to 4. At 4, 5 is larger and 3 is smaller, so the values are split and the answer is 4.

    Each pass moves one level down, so the walk makes at most h passes. A recursive version would use O(h) stack space; a loop that only moves a pointer uses none.

    Step by step

    1. Start a pointer at the root. node is the node we are looking at; the root is a common ancestor, so we are never above the answer.
    2. Loop while node is not null. Each pass either returns or moves down one level.
    3. Both values smaller: join the two comparisons with && and move node to node->left.
    4. Both values larger: the mirrored case, in an else if, moves to node->right.
    5. Anything else: the values are on different sides, or one equals the node's value. Return node->val.
    6. Return -1 after the loop. Because p and q are in the tree, the loop always returns first. The line only gives the function a return value on every path.

    The solution in C++

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

    The C++ details worth a closer look:

    • Values, not nodes. p and q are plain int values, so they are compared directly with node->val. Nothing needs to be dereferenced.
    • NULL in the loop condition. A pointer is compared with NULL here, as in the lesson's TreeNode definition. nullptr is the modern spelling and works the same way.
    • Only a pointer moves. node = node->left changes the local pointer. It doesn't modify the tree and allocates nothing.
    • The final return -1. A non-void function that can reach its end without a return has undefined behaviour in C++ if that path is taken, and compilers warn about it. The extra line is dead code here, but it keeps the function well-formed. The value -1 is safe as a marker only because it's unreachable; node values can be negative too.

    Complexity

    Time: O(h). Every pass descends one level and the loop ends at the latest at the node where the paths to p and q split. For a balanced tree that is O(log n); for a tree that is a single chain it is O(n).

    Space: O(1). One pointer and no recursion.

    Edge cases and common mistakes

    • One value is an ancestor of the other. For p = 2, q = 4, at node 2 the check p < 2 fails, and so does p > 2, so the code returns 2. Using <= or >= in the first two branches would walk past the answer.
    • Equal values. p = q = 3 walks down to the node 3 and returns 3.
    • A single-node tree. The root is returned immediately.
    • Assuming the answer is always in the middle. It can be the root, one of the two values, or any node between them.
    • Using the BST property on the wrong tree. The direction rule relies on the ordering. In an unordered binary tree, comparing values says nothing about which subtree they are in.
    • Mixing up the two checks. Joining them with || would turn when only one value is smaller into a move left, which can lose the other value.

    Testing

    On the tree [6,2,8,0,4,7,9,NULL,NULL,3,5]:

    • p = 2, q = 8 gives 6.
    • p = 2, q = 4 gives 2.
    • p = 5, q = 3 gives 4.
    • p = 3, q = 3 gives 3.
    • p = 0, q = 5 gives 2.
    Keep going with C++650+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free