The simplest approach is to collect all node values from the tree and then sort them based on their distance to the target. Once sorted, the first k elements are the closest values. This works because we just need the k values closest to the target, regardless of their original position in the tree.
class Solution:
def closestKValues(self, root: TreeNode, target: float, k: int) -> List[int]:
def dfs(node, arr):
if not node:
return
arr.append(node.val)
dfs(node.left, arr)
dfs(node.right, arr)
arr = []
dfs(root, arr)
arr.sort(key = lambda x: (abs(x - target), x))
return arr[:k]Where is the number of nodes in the tree
Instead of sorting all values, we can use a max-heap of size k to track the k closest values seen so far. As we traverse the tree, we compare each node's distance to the target with the farthest element in our heap. If the current node is closer, we replace the farthest element. This avoids sorting the entire array.
k, remove the element with the largest distance.class Solution:
def closestKValues(self, root: TreeNode, target: float, k: int) -> List[int]:
def dfs(node, heap):
if not node:
return
if len(heap) < k:
heappush(heap, (-abs(node.val - target), node.val))
else:
if abs(node.val - target) <= abs(heap[0][0]):
heappop(heap)
heappush(heap, (-abs(node.val - target), node.val))
dfs(node.left, heap)
dfs(node.right, heap)
heap = []
dfs(root, heap)
return [x[1] for x in heap]Where is the number of nodes in the tree and is the size of our heap
Since this is a BST, an inorder traversal gives us values in sorted order. With a sorted array, the k closest values to the target form a contiguous subarray. We can use binary search to find the position closest to the target, then expand outward using two pointers to collect the k nearest values.
left pointing to the element just before the insertion point, right pointing at the insertion point.k elements are collected.class Solution:
def closestKValues(self, root: TreeNode, target: float, k: int) -> List[int]:
def dfs(node, arr):
if not node:
return
dfs(node.left, arr)
arr.append(node.val)
dfs(node.right, arr)
arr = []
dfs(root, arr)
left = bisect_left(arr, target) - 1
right = left + 1
ans = []
while len(ans) < k:
if right == len(arr) or abs(arr[left] - target) <= abs(arr[right] - target):
ans.append(arr[left])
left -= 1
else:
ans.append(arr[right])
right += 1
return ansWhere is the number of nodes in the tree and is the size of our sliding window
Since the inorder traversal produces a sorted array and we need a contiguous subarray of size k, we can binary search for the optimal starting position of this window. For any starting position, we compare the distances of the leftmost and rightmost elements in the window to decide if shifting right would improve our answer.
k-element window. The search range is from index 0 to n-k.mid with the element at mid+k. If the element at mid+k is closer, shift the window right; otherwise, keep searching left.k.class Solution:
def closestKValues(self, root: TreeNode, target: float, k: int) -> List[int]:
def dfs(node, arr):
if not node:
return
dfs(node.left, arr)
arr.append(node.val)
dfs(node.right, arr)
arr = []
dfs(root, arr)
left = 0
right = len(arr) - k
while left < right:
mid = (left + right) // 2
if abs(target - arr[mid + k]) < abs(target - arr[mid]):
left = mid + 1
else:
right = mid
return arr[left:left + k]Where is the number of nodes in the tree and is the number of closest values to return
During inorder traversal, values are visited in sorted order. We can maintain a sliding window of size k using a deque. As we visit each node, we add it to the window. When the window exceeds k elements, we compare the distances of the first and last elements and remove the one farther from the target. Once the first element is closer, all subsequent elements will be even farther, so we can stop early.
k, compare the front and back elements. If the front is closer or equal, remove the back and stop traversing the right subtree. Otherwise, remove the front and continue.class Solution:
def closestKValues(self, root: TreeNode, target: float, k: int) -> List[int]:
def dfs(node, queue):
if not node:
return
dfs(node.left, queue)
queue.append(node.val)
if len(queue) > k:
if (abs(target - queue[0]) <= abs(target - queue[-1])):
queue.pop()
return
else:
queue.popleft()
dfs(node.right, queue)
queue = deque()
dfs(root, queue)
return list(queue)Where is the number of nodes in the tree and is the number of closest values to return
Using a generic tree traversal and sorting all values works but misses the O(n + k) optimization possible with BST. Inorder traversal gives sorted values, enabling efficient sliding window approaches.
When using binary search to find the starting position for the sliding window, the insertion point may be at index 0 or n, causing out-of-bounds access when initializing the left pointer.
# Wrong: Can cause index -1 access
left = bisect_left(arr, target) - 1 # If target < all elements, left = -1
# Must check: if left < 0 before accessing arr[left]When using a heap to track k closest values, a max-heap is needed to evict the farthest element. Using a min-heap evicts the closest element instead, producing incorrect results.
# Wrong: Min-heap evicts closest values
heappush(heap, (abs(node.val - target), node.val))
# Correct: Max-heap (negate distance) evicts farthest
heappush(heap, (-abs(node.val - target), node.val))When two values have equal distance to target, the problem may require returning the smaller value. Failing to handle ties correctly can produce wrong results.
In the sliding window approach, stopping as soon as one pointer goes out of bounds forgets to collect remaining elements from the other side.