Practice
from typing import Optional
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def kthSmallest(root: Optional[TreeNode], k: int) -> int:
stack = []
current = root
count = 0
while current or stack:
while current:
stack.append(current)
current = current.left
current = stack.pop()
count += 1
if count == k:
return current.val
current = current.right
# Tree:
# 3
# / \
# 1 4
# \
# 2
root = TreeNode(3)
root.left = TreeNode(1)
root.right = TreeNode(4)
root.left.right = TreeNode(2)
print(kthSmallest(root, 2))
Solution
Step 1: Trace the inorder traversal steps
Stack initially empty, current=root(3). Push 3, move left to 1. Push 1, move left to None. Pop 1 (count=1), move right to 2.Step 2: Continue traversal to find kth=2
Push 2, move left None. Pop 2 (count=2), return 2 as kth smallest.Final Answer:
Option C -> Option CQuick Check:
Second smallest in BST is 2 [OK]
- Off-by-one counting of k
- Returning before incrementing count
Solution
Step 1: Understand BST property usage
In a BST, left subtree nodes are smaller and right subtree nodes are larger than the current node. This allows pruning the search space efficiently.Step 2: Identify optimal traversal
Starting from root, if both target nodes are smaller, go left; if both are larger, go right; else current node is the split point and thus the LCA.Final Answer:
Option C -> Option CQuick Check:
Iterative BST traversal uses O(h) time and O(1) space [OK]
- Using path comparison wastes time and space
- Greedy approach ignores split point logic
- Dynamic programming is unnecessary overhead
Solution
Step 1: Understand the problem constraints
The tree is a BST, so left subtree nodes are less than the node, right subtree nodes are greater.Step 2: Identify the optimal search approach
Using the BST property allows skipping half of the tree at each step, leading to O(h) time complexity.Final Answer:
Option A -> Option AQuick Check:
BST property guides search direction efficiently [OK]
- Thinking BFS or full traversal is optimal for BST search
def deleteNode(root, key):
if not root:
return null
if key < root.val:
root.left = deleteNode(root.left, key)
elif key > root.val:
root.right = deleteNode(root.right, key)
else:
if not root.left:
return root.right
elif not root.right:
return root.left
else:
successor = findSuccessor(root)
root.val = successor.val
root.left = deleteNode(root.right, successor.val) # Bug here
return rootSolution
Step 1: Identify two-children deletion logic
When deleting a node with two children, we replace its value with the successor's value and then delete the successor from the right subtree.Step 2: Spot incorrect subtree assignment
The line assigns root.left = deleteNode(root.right, successor.val), which incorrectly deletes from the right subtree but assigns result to left child, breaking BST structure.Final Answer:
Option A -> Option AQuick Check:
Correct line should assign root.right = deleteNode(root.right, successor.val) [OK]
- Mixing left and right subtree assignments
- Forgetting to update parent's child pointer
- Replacing value but not deleting successor
def insertIntoBST(root, val):
if root is None:
return TreeNode(val)
curr = root
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
Solution
Step 1: Understand duplicate insertion rule
Duplicates must go to the left subtree, so values equal to current node should be treated as less or equal.Step 2: Modify comparison operator
Changing condition to val <= curr.val ensures duplicates follow left subtree insertion path, preserving BST property.Final Answer:
Option A -> Option AQuick Check:
Duplicates inserted left by using <= comparison [OK]
- Inserting duplicates right breaks problem requirement
- Inserting duplicates as new roots breaks BST
- Ignoring duplicates causes infinite recursion
