Lowest Common Ancestor in a Binary Search Tree
AmazonSoftware Engineer · Technical ScreenLast updated
Tests finding the lowest common ancestor of two values in a Binary Search Tree represented as a level-order serialized list, accounting for self-ancestorship and missing-node edge cases.
Lowest Common Ancestor in a Binary Search Tree
You are given the level-order serialization of a Binary Search Tree (BST) containing unique integer values, along with two query values p and q.
The Lowest Common Ancestor (LCA) between two nodes u and v is defined as the lowest node T in the tree that has both u and v as descendants. Under standard edge semantics, a node can be a descendant of itself (for example, if p is an ancestor of q, the LCA is p).
Your task is to return the value of the LCA of p and q. If either p or q (or both) does not exist in the tree, return None.
Input Format
tree: A list representing the standard level-order (breadth-first) traversal of the BST, whereNoneindicates a missing child node. TrailingNonevalues may be omitted.p: An integer representing the first target value.q: An integer representing the second target value.
Function Signature
def lowest_common_ancestor(tree: list[int | None], p: int, q: int) -> int | None:
pass
Constraints
0 <= len(tree) <= 10^5-10^9 <= tree[i] <= 10^9for non-null elements.- All non-null elements in
treeform a valid Binary Search Tree. -10^9 <= p, q <= 10^9pandqmay or may not be equal.
Examples
Example 1:
Input:
tree = [6, 2, 8, 0, 4, 7, 9, None, None, 3, 5]
p = 2
q = 8
Output: 6
Explanation:
The root node with value 6 has 2 in its left subtree and 8 in its right subtree, making 6 the lowest common ancestor.
Example 2:
Input:
tree = [6, 2, 8, 0, 4, 7, 9, None, None, 3, 5]
p = 2
q = 4
Output: 2
Explanation:
Node 2 is an ancestor of node 4. Under self-ancestor semantics, the LCA of 2 and 4 is 2.
Example 3:
Input:
tree = [6, 2, 8, 0, 4, 7, 9, None, None, 3, 5]
p = 2
q = 10
Output: None
Explanation:
Node 10 does not exist in the BST, so no common ancestor exists.
Edge Cases to Consider
- An empty
tree([]) should returnNone. p == q: If the node exists in the tree, its LCA with itself is itself. If it does not exist, returnNone.- One or both target values do not exist in the BST.
Hints
Hint 1
First reconstruct the tree structure from the level-order serialization using a queue, or verify node existence during an initial traversal.
Hint 2
Do not assume both nodes exist in the tree; you must verify that both `p` and `q` are present before or during your LCA computation.
Hint 3
For a valid BST, starting at the root: if both `p` and `q` are strictly smaller than the current node, move left; if both are strictly greater, move right; otherwise, the current node is the candidate split point.
Follow-up questions
- How can you find the LCA directly from the level-order array without fully allocating `TreeNode` objects in memory?
- If the BST contains duplicate values (e.g., left <= root < right), how would that change your traversal logic?
- How would you optimize queries if the tree is static and you receive millions of LCA queries for different pairs `(p, q)`?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.