Lowest Common Ancestor in Binary Tree in DSA Javascript - Time & Space Complexity
We want to understand how the time needed to find the lowest common ancestor changes as the tree grows.
How does the search through the tree affect the time it takes?
Analyze the time complexity of the following code snippet.
function lowestCommonAncestor(root, p, q) {
if (!root || root === p || root === q) return root;
const left = lowestCommonAncestor(root.left, p, q);
const right = lowestCommonAncestor(root.right, p, q);
if (left && right) return root;
return left ? left : right;
}
This code finds the lowest common ancestor of two nodes in a binary tree by searching both sides recursively.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive calls visiting each node once.
- How many times: Each node is visited once during the search.
As the number of nodes grows, the function checks each node once, so the work grows directly with the number of nodes.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 node visits |
| 100 | About 100 node visits |
| 1000 | About 1000 node visits |
Pattern observation: The time grows linearly with the number of nodes in the tree.
Time Complexity: O(n)
This means the time to find the ancestor grows in direct proportion to the number of nodes in the tree.
[X] Wrong: "The function only visits part of the tree, so it runs faster than O(n)."
[OK] Correct: The function may need to visit every node in the worst case to find both targets, so it still takes time proportional to the whole tree.
Understanding this helps you explain how recursive tree searches work and why visiting all nodes is sometimes necessary, a key skill in many coding challenges.
"What if the tree was a binary search tree? How would that change the time complexity?"
Practice
Lowest Common Ancestor (LCA) of two nodes in a binary tree?Solution
Step 1: Understand ancestor concept
An ancestor of a node is any node on the path from the root to that node.Step 2: Define lowest common ancestor
The lowest common ancestor is the deepest node that is an ancestor of both given nodes.Final Answer:
The deepest node that is an ancestor of both nodes -> Option CQuick Check:
LCA = deepest common ancestor [OK]
- Confusing LCA with root node
- Choosing node with smallest value
- Selecting node based on traversal order
Solution
Step 1: Identify base case for recursion
When the current node is null, it means we've reached the end of a path without finding the target nodes.Step 2: Correct base case condition
Return null to indicate no ancestor found in this path.Final Answer:
if (root == null) return null; -> Option DQuick Check:
Base case returns null on null node [OK]
- Returning root when left or right child is null
- Checking root.value == null which is invalid
- Missing base case leading to infinite recursion
3
/ \
5 1
/ \ / \
6 2 0 8
function findLCA(root, p, q) {
if (!root || root === p || root === q) return root;
const left = findLCA(root.left, p, q);
const right = findLCA(root.right, p, q);
if (left && right) return root;
return left ? left : right;
}
const lca = findLCA(root, node5, node1);
console.log(lca.value);What will be printed?Solution
Step 1: Trace recursive calls for nodes 5 and 1
Both nodes 5 and 1 are children of root 3, so recursion finds them in left and right subtrees respectively.Step 2: Determine LCA based on recursion
Since left and right recursive calls return non-null (nodes 5 and 1), the current root 3 is the LCA.Final Answer:
3 -> Option BQuick Check:
LCA of 5 and 1 is 3 [OK]
- Returning one of the nodes instead of root
- Confusing node values with LCA
- Ignoring both left and right recursive results
function findLCA(root, p, q) {
if (!root) return null;
if (root === p || root === q) return root;
const left = findLCA(root.left, p, q);
const right = findLCA(root.right, p, q);
if (left) return left;
if (right) return right;
return null;
}Solution
Step 1: Analyze return conditions after recursion
The code returns left if left is non-null without checking right, missing the case when both are non-null.Step 2: Identify missing combined check
It should return root if both left and right are non-null, indicating LCA found at current root.Final Answer:
It returns left child even if right child is also non-null -> Option AQuick Check:
Must check both sides before returning [OK]
- Returning left without checking right
- Missing combined condition for LCA
- Ignoring null root check
Solution
Step 1: Understand tree structure
Nodes 2 and 4 are in left subtree of 3; nodes 5 and 6 are in right subtree of 3.Step 2: Find common ancestor of nodes 2 and 6
Since 2 is in left subtree and 6 is in right subtree, their lowest common ancestor is the root node 3.Final Answer:
3 -> Option AQuick Check:
LCA of nodes in opposite subtrees is root [OK]
- Choosing one of the nodes instead of ancestor
- Confusing sibling nodes as LCA
- Ignoring tree structure
Ready to prove it?
Pass the test and this pattern moves unknown
