Given the root of a binary search tree, a target value, and an integer k, return the k values in the BST that are closest to the target. The answer can be returned in any order and it is guaranteed that there is only one unique set of k values closest to the target.
Key Insights
The BST property allows efficient narrowing down of candidate nodes.
Using two stacks to track predecessors (values <= target) and successors (values > target) can efficiently yield the closest values.
A modified in-order traversal helps in retrieving the next closest candidate from either side.
Merging two sorted lists of candidate values based on absolute difference with the target is the key step.
Space and Time Complexity
Time Complexity: O(k + log n) – O(log n) to initialize the two stacks and O(k) to collect k elements.
Space Complexity: O(log n) – due to the stack space in a balanced BST.
Solution
We can solve the problem by splitting it into two parts:
Build two stacks:
The predecessor stack for nodes with values less than or equal to target.
The successor stack for nodes with values greater than target.
To build these stacks, traverse the BST starting from the root and push nodes along the path: if the node's value is less than or equal to target, push it to the predecessor stack and go right; if greater, push it to the successor stack and go left.
Merge the two stacks:
At each step, compare the top element of the predecessor stack (if available) and the successor stack (if available) based on their distance from the target.
Pop from the stack with the closer candidate and add that value to the result list.
For the popped node, update the given stack by moving to the next appropriate node in the in-order traversal (for predecessor, go to left child then rightmost descendant; for successor, go to right child then leftmost descendant).
Repeat until k values have been collected.
Key Data Structures and Techniques:
Two stacks to simulate reverse and forward in-order traversals.
Merge technique to choose the closest candidate at each iteration.
Code Solutions
# Python solution for Closest Binary Search Tree Value II# Definition for a binary tree node.classTreeNode:def__init__(self, val=0, left=None, right=None): self.val = val
self.left = left
self.right = right
classSolution:defclosestKValues(self, root: TreeNode, target:float, k:int):# Helper function to initialize predecessors stackdefinitPredecessor(node):while node:# If node value is less or equal, push to preds and go rightif node.val <= target: preds.append(node) node = node.right
else: node = node.left
# Helper function to initialize successors stackdefinitSuccessor(node):while node:# If node value is greater than target, push to succs and go leftif node.val > target: succs.append(node) node = node.left
else: node = node.right
# Helper function to get next predecessor valuedefgetPredecessor():ifnot preds:returnNone node = preds.pop() val = node.val
# Traverse the left subtree: push nodes from left child then rightmost branch node = node.left
while node: preds.append(node) node = node.right
return val
# Helper function to get next successor valuedefgetSuccessor():ifnot succs:returnNone node = succs.pop() val = node.val
# Traverse the right subtree: push nodes from right child then leftmost branch node = node.right
while node: succs.append(node) node = node.left
return val
preds, succs =[],[]# Initialize the stacks with correct boundary values initPredecessor(root) initSuccessor(root) result =[]# If the closest value from predecessor and successor are same, skip one to avoid duplicates.if preds and succs and preds[-1].val == succs[-1].val: getPredecessor()# Merge k closest valuesfor _ inrange(k):ifnot preds:# Only successors left result.append(getSuccessor())elifnot succs:# Only predecessors left result.append(getPredecessor())else:# Compare which candidate is closer to the targetifabs(preds[-1].val - target)<=abs(succs[-1].val - target): result.append(getPredecessor())else: result.append(getSuccessor())return result