Start learning free

LESSON 340 OF 438 · BINARY TREES

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

Practice O(n) Read the full explanation

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

The lowest common ancestor of p and q is the deepest node that has both of them in its subtree. A node counts as its own ancestor: if the node holding p is above the node holding q, the answer is p.

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

The trees below are written in level order (top to bottom, left to right), with null for a missing child.

 

Example 1:

        6
      /   \
     2     8
    / \   / \
   0   4 7   9
      / \
     3   5

Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
Output: 6
Explanation: 2 is in the left subtree of 6 and 8 is in its right subtree, so 6 is the deepest node that has both below it.

Example 2:

Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
Output: 2
Explanation: 4 is in the subtree of 2, and a node counts as its own ancestor.

Example 3:

Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 5, q = 3
Output: 4
Explanation: 3 and 5 are the two children of 4, one on each side.

 

Constraints:

  • The number of nodes in the tree is in the range [1, 1000].
  • Each node value is between -104 and 104, and all values are different.
  • p and q are both values of nodes in the tree. They can be equal.

 

Follow up: Can you find it in O(h) time, where h is the height of the tree, using only O(1) extra space?
STEP 1 OF 6
    Ctrl + Enter to check

    JAVASCRIPT · BINARY TREES

    Lca In Bst in JavaScript, explained

    Problem overview

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

    Take the tree with root 6, children 2 and 8, then 0 and 4 under 2, 7 and 9 under 8, and 3 and 5 under 4.

    • For p = 2, q = 8 the answer is 6: one value is in the left subtree and the other in the right one, so no deeper node holds both.
    • For p = 2, q = 4 the answer is 2, because 4 is below 2 and 2 is its own ancestor.
    • For p = 5, q = 3 the answer is 4, their shared parent.

    The follow-up asks for O(h) time, where h is the height of the tree, with O(1) extra space.

    Approach: let the values choose the direction

    For a general binary tree you would have to search both subtrees, because nothing tells you where a value is. A BST does tell you: every value smaller than a node is in its left subtree, and every larger value is in its right subtree.

    The root is above everything, so it is a common ancestor of p and q. The question is whether a lower one exists. Look at the current node and compare it with both values:

    • If both values are smaller than the node, both are in its left subtree, and so is their lowest common ancestor. Move left.
    • If both are larger, move right for the same reason.
    • Otherwise the two values can't both be on one side. Either one is smaller and the other larger, or one of them equals the node's value. In both cases this node has both below it or at it, and moving to either child would leave one of them behind. This node is the answer.

    Each pass goes one level down and never goes back, so the walk takes at most h passes. It needs no recursion and no stack, only the pointer to the current node.

    On the example with p = 5 and q = 3: at 6 both values are smaller, so move to 2. At 2 both are larger, so move to 4. At 4, 5 is larger and 3 is smaller, so the walk stops and returns 4.

    Step by step

    1. Declare node with let and set it to root. It is the pointer that moves down the tree, so it can't be a const.
    2. Write a while (node) loop. A missing child is null, which is falsy, so the loop ends if the walk ever falls off the tree. Each pass handles one node.
    3. If both values are smaller than node.val, move to node.left. The two checks are joined with &&: moving left is only safe when both values are smaller.
    4. Otherwise, if both are larger, move to node.right. This is the mirror image of the previous step.
    5. In the final else, return node.val. This branch covers a split between the two sides and also the case where p or q equals the node's value.
    6. After the loop, return -1. Because p and q are in the tree, the loop always returns first. The line only guarantees that the method returns a number on every path.

    The solution in JavaScript

    class Solution {
        lowestCommonAncestor(root, p, q) {
            let node = root;
            while (node) {
                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;
        }
    }

    Some details about JavaScript:

    • p and q are plain numbers, not nodes. The comparisons are numeric (p < node.val), and the method returns node.val, a number.
    • while (node) relies on truthiness. A tree node is an object, which is always truthy, and null is falsy, so the loop runs exactly while node points at a real node. Node values don't matter here: a node holding 0 is still an object and still truthy.
    • The test for "both smaller" must come with &&, not ||. With ||, a node that splits the two values would be skipped.

    Complexity

    Time is O(h): each pass moves one level down, so there are at most h passes with constant work in each. For a balanced tree that is about log n passes, and for a tree that is a single chain of n nodes it is n. Extra space is O(1), since the method only keeps the pointer node.

    Edge cases and common mistakes

    • One value is an ancestor of the other. The walk stops at the ancestor, because that node's value is neither smaller nor larger than itself. With p = 2 and q = 4 the answer is 2.
    • p equals q. The walk heads down to the node holding that value and returns it. With p = q = 7 the answer is 7.
    • A single-node tree. The first pass reaches the else branch and returns the root's value.
    • Using <= or >= in the two checks. That would send the walk past a node that equals p or q, which is the answer.
    • Searching the whole tree. A full traversal works but costs O(n) and ignores the BST ordering.

    Testing

    On the example tree, lowestCommonAncestor(root, 2, 8) returns 6, (root, 2, 4) returns 2, (root, 5, 3) returns 4, (root, 7, 7) returns 7 and (root, 3, 9) returns 6.

    Keep going with JavaScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free