💡 The loop continues until the LCA is found or the tree is fully traversed.
code_explanation
Code walkthrough: Move left or right
If both p and q are less than current, move left; if both greater, move right.
💡 This step shows how the BST property guides the traversal direction.
Line:if p.val < current.val and q.val < current.val:
current = current.left
elif p.val > current.val and q.val > current.val:
current = current.right
💡 The algorithm prunes the search space by moving only in one direction.
code_explanation
Code walkthrough: Found LCA
When p and q split to different sides or match current, return current as LCA.
💡 This final step shows the stopping condition of the algorithm.
Line:else:
return current
💡 The LCA is the node where the search stops because p and q diverge.
def lowestCommonAncestor(root, p, q):
current = root # STEP 1: Initialize current to root
while current: # STEP 2: Loop while current is not None
if p.val < current.val and q.val < current.val: # STEP 3: Check if both p and q are less than current
current = current.left # STEP 3: Move left
elif p.val > current.val and q.val > current.val: # STEP 4: Check if both p and q are greater than current
current = current.right # STEP 4: Move right
else: # STEP 5: Found split point or match
return current # STEP 5: Return current as LCA
# Example usage:
# print(lowestCommonAncestor(root, p, q).val)
📊
Lowest Common Ancestor of a BST - Watch the Algorithm Execute, Step by Step
Watching each step helps you understand how the BST property guides the search efficiently without exploring unnecessary nodes.
Step 1/10
·Active fill★Answer cell
Current node: 1
628047935
Current node: 1
628047935
Current node: 1
628047935
Current node: 1
628047935
Return: 1
628047935
Return: 1
628047935
Return: 1
628047935
Return: 1
628047935
Return: 1
628047935
Return: 1
628047935
Return: 1
Key Takeaways
✓ The BST property allows the algorithm to decide direction at each node, drastically reducing search space.
This insight is hard to see from code alone because the efficiency comes from the tree structure, not just the code logic.
✓ The LCA is found at the first node where p and q values split to different subtrees or match the current node.
Understanding this split point is key to grasping why the algorithm stops early.
✓ The algorithm never needs to explore both subtrees fully, making it optimal for BSTs.
This is a concrete example of how BSTs enable efficient queries compared to general binary trees.
Practice
(1/5)
1. Given the following code for inserting a value into a BST, what will be the value of the variable parent.val when inserting 5 into the BST rooted at 4 with left child 2 and right child 7?
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def insertIntoBST(root, val):
if root is None:
return TreeNode(val)
curr = root
parent = None
while curr:
parent = curr
if val < curr.val:
if curr.left is None:
curr.left = TreeNode(val)
return root
curr = curr.left
else:
if curr.right is None:
curr.right = TreeNode(val)
return root
curr = curr.right
return root
root = TreeNode(4)
root.left = TreeNode(2)
root.right = TreeNode(7)
val = 5
insertIntoBST(root, val)
easy
A. 4
B. 7
C. 2
D. 5
Solution
Step 1: Trace insertion path
Start at root (4). Since 5 > 4, move right to node 7. Update parent to 4.
Step 2: Check right child of 7
5 < 7, so move left. Left child of 7 is None, so insert here. Parent is now 7, but last parent before insertion was 4.
Final Answer:
Option A -> Option A
Quick Check:
Parent is last non-null node before insertion -> 4 [OK]
Hint: Parent tracks last node before insertion point [OK]
Common Mistakes:
Confusing parent with current node
Off-by-one in loop iteration
Mistaking inserted value as parent
2. Consider the following code snippet for searching a value in a BST. What will be printed when searching for the value 3 in the given tree?
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def searchBST(root, val):
current = root
while current:
if current.val == val:
return current
elif val < current.val:
current = current.left
else:
current = current.right
return None
root = TreeNode(4, TreeNode(2, TreeNode(1), TreeNode(3)), TreeNode(7))
result = searchBST(root, 3)
print(result.val if result else 'null')
easy
A. 2
B. 7
C. null
D. 3
Solution
Step 1: Trace the search path
Start at root (4). Since 3 < 4, move to left child (2). Since 3 > 2, move to right child (3).
Step 2: Check node value
Current node value is 3, which matches the search value, so return this node.
Final Answer:
Option D -> Option D
Quick Check:
Search follows BST property correctly and finds node with value 3 [OK]
Hint: Follow BST property to move left or right [OK]
Common Mistakes:
Returning wrong node value due to off-by-one traversal
3. Given the following code for trimming a BST and the input tree with root value 3, left child 1, right child 4, and low=2, high=3, what is the value of the root node after trimming?
easy
A. 2
B. 3
C. 1
D. 4
Solution
Step 1: Move root to valid range
Root=3 is within [2,3], so no change.
Step 2: Trim left subtree
Left child=1 < 2, so replace left child with its right subtree (None). Left child becomes None.
Step 3: Trim right subtree
Right child=4 > 3, so replace right child with its left subtree (None). Right child becomes None.
Final Answer:
Option B -> Option B
Quick Check:
Root remains 3 with no children after trimming [OK]
Hint: Check root adjustment before subtree trimming [OK]
Common Mistakes:
Assuming root changes when already in range
Forgetting to trim subtrees correctly
4. What is the average time complexity per call of next() in the Morris traversal based BST Iterator, assuming the BST has n nodes?
medium
A. O(1) amortized per next() call because each edge is visited at most twice.
B. O(n) per next() call because each call may traverse the entire tree.
C. O(h) per next() call where h is the tree height due to traversing left children.
D. O(log n) per next() call assuming a balanced BST and stack usage.
Solution
Step 1: Understand Morris traversal edge visits
Each edge in the BST is visited at most twice: once to create a thread and once to remove it.
Step 2: Calculate amortized cost per next()
Since total work is O(n) for n nodes, and there are n calls to next(), average cost per call is O(1) amortized.
Final Answer:
Option A -> Option A
Quick Check:
Amortized O(1) per next() matches Morris traversal properties [OK]
Hint: Morris traversal visits edges twice total [OK]
Common Mistakes:
Assuming worst-case O(n) per call
Confusing with stack-based O(h)
Ignoring amortized analysis
5. What is the worst-case time complexity of inserting a value into a binary search tree using the iterative insertion method shown below?
def insertIntoBST(root, val):
if root is None:
return TreeNode(val)
curr = root
while curr:
if val < curr.val:
if curr.left is None:
curr.left = TreeNode(val)
return root
curr = curr.left
else:
if curr.right is None:
curr.right = TreeNode(val)
return root
curr = curr.right
return root
medium
A. O(1) because insertion is done at a leaf immediately
B. O(h) where h is the height of the tree, since traversal down the tree is needed
C. O(n) where n is the number of nodes, because all nodes might be visited
D. O(log n) always, because BSTs are balanced by definition
Solution
Step 1: Identify traversal cost
Insertion requires traversing from root down to a leaf where the new node is inserted.
Step 2: Relate traversal to tree height
In worst case, the tree is skewed, so height h can be up to n, but complexity is expressed as O(h).
Final Answer:
Option B -> Option B
Quick Check:
Traversal cost dominates insertion -> O(h) [OK]
Hint: Insertion cost depends on tree height, not always balanced [OK]
Common Mistakes:
Assuming O(1) because insertion is at leaf
Confusing height h with number of nodes n
Assuming BST is always balanced
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.