LeetCode 272: Closest BST Value II Solution in Python – A Step-by-Step Guide

Imagine you’re wandering through a binary search tree (BST), where numbers are neatly sorted—smaller ones on the left, bigger ones on the right—and you need to find not just one, but the k closest values to a target number, like picking the k nearest stars in a twinkling sky. That’s the exciting challenge of LeetCode 272: Closest BST Value II! This hard-level problem asks you to return the k values in a BST that are closest to a given target float. Using Python, we’ll explore two solutions: the Best Solution, an inorder traversal with a sliding window that’s fast and efficient, and an Alternative Solution, a min-heap approach that’s straightforward but heavier. With detailed examples, clear code, and friendly, easy-to-follow explanations—especially for the best solution—this guide will help you master BST searches and boost your coding skills. Let’s start finding those k closest values!

What Is LeetCode 272: Closest BST Value II?

Section link icon

In LeetCode 272: Closest BST Value II, you’re given the root of a binary search tree (BST), a target value (a float), and an integer k. Your task is to return a list of the k integer values in the tree closest to the target, in any order. In a BST, left children are less than their parent, and right children are greater, making it ideal for ordered searches. This problem builds on LeetCode 270: Closest BST Value, expanding from one closest value to k closest values.

Problem Statement

  • Input: A BST root node, a float target, and an integer k.
  • Output: A list of k integers—the k values in the BST closest to target.
  • Rules: BST properties hold (left < node < right); return k values with smallest absolute differences; order doesn’t matter.

Constraints

  • Number of nodes: 1 to 10^4.
  • Node values: -10^9 to 10^9.
  • target: -10^9 to 10^9 (float).
  • k: 1 to total nodes.

Examples

  • Input:
    • Tree: Root: 4, Left: 2 (Left: 1, Right: 3), Right: 5
    • Target: 3.714286
    • k: 2
    • Output: [4, 3] (closest: 4 (|0.285714|), 3 (|0.714286|)).
  • Input:
    • Tree: Root: 1
    • Target: 0.0
    • k: 1
    • Output: [1] (only value).
  • Input:
    • Tree: Root: 3, Left: 2, Right: 4
    • Target: 2.5
    • k: 2
    • Output: [2, 3] (|2 - 2.5| = 0.5, |3 - 2.5| = 0.5).

Understanding the Problem: Picking k Closest Values

Section link icon

To solve LeetCode 272: Closest BST Value II in Python, we need to find the k BST values with the smallest absolute differences from a target float. The BST’s ordering (left < node < right) helps us search efficiently, but we need k values, not just one. A basic way—collecting all values and sorting by difference—works but wastes time (O(n log n)). We’ll use two methods: 1. Best Solution (Inorder Traversal with Sliding Window): O(n) time—fast and smart. 2. Alternative Solution (Min-Heap): O(n log k) time—clear but heavier.

Let’s dive into the best solution with a friendly, detailed walkthrough.

Best Solution: Inorder Traversal with Sliding Window

Section link icon

Why This Is the Best Solution

The inorder traversal with sliding window approach is the top pick for LeetCode 272 because it runs in O(n) time—where n is the number of nodes—and uses O(k) space, leveraging the BST’s sorted nature to keep only the k closest values without extra sorting. It’s like walking through the tree in order, keeping a short list of the k best matches, and sliding out the farthest as you go, making it both quick and memory-efficient.

How It Works

Picture this solution as strolling through the BST in order (smallest to largest), carrying a little basket that holds k numbers. As you pick up each new value, you check if it’s closer to the target than the farthest in your basket—if so, swap it in and slide the old one out. Here’s how it works, step-by-step, explained simply:

  • Step 1: Use Inorder Traversal:
    • In a BST, inorder traversal (left, root, right) visits nodes in ascending order.
    • Use a stack to walk the tree iteratively, collecting values from smallest to largest.
  • Step 2: Keep a Window of k Values:
    • Use a deque (double-ended queue) to hold k values, adding new ones and removing the farthest when full.
    • Track differences from the target to decide what stays.
  • Step 3: Slide the Window:
    • For each new value:
      • If deque < k, add it.
      • If deque = k, compare new value’s diff with the farthest (leftmost if ascending); if closer, pop left, append right.
  • Step 4: Stop Early (Optimization):
    • Since values increase, if a new value’s diff exceeds the farthest in the deque, all remaining values will be farther—stop.
  • Step 5: Return the k Closest:
    • The deque holds the k closest values.

It’s like picking k best friends—keep the closest, swap out the farthest as you meet new ones!

Step-by-Step Example

Example: Tree: Root: 4, Left: 2 (Left: 1, Right: 3), Right: 5; Target: 3.714286; k: 2

  • Text Representation:
    • Root: 4
    • Left: 2
      • Left: 1
      • Right: 3
    • Right: 5
  • Step 1: Inorder: 1, 2, 3, 4, 5.
  • Step 2: Deque starts empty, k = 2.
  • Step 3: Process values:
    • 1: Diff = |1 - 3.714286| ≈ 2.714286, Deque = [1].
    • 2: Diff ≈ 1.714286, Deque = [1, 2].
    • 3: Diff ≈ 0.714286, Farthest (1) = 2.714286 > 0.714286, Deque = [2, 3].
    • 4: Diff ≈ 0.285714 < 1.714286 (farthest 2), Deque = [3, 4].
    • 5: Diff ≈ 1.285714 > 0.714286 (farthest 3), stop (ascending, all worse).
  • Step 4: Deque = [3, 4].
  • Result: [3, 4].

Code with Detailed Line-by-Line Explanation

Here’s the Python code, explained in a friendly way:

from collections import deque

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def closestKValues(self, root: TreeNode, target: float, k: int) -> List[int]:
        # Step 1: Set up deque for k closest
        window = deque()

        # Step 2: Inorder traversal with stack
        stack = []
        curr = root
        while curr or stack:
            # Go left as far as possible
            while curr:
                stack.append(curr)
                curr = curr.left

            # Process current node
            curr = stack.pop()
            val = curr.val
            diff = abs(val - target)

            # Step 3: Manage sliding window
            if len(window) < k:
                window.append(val)  # Add if less than k
            else:
                # Compare with farthest (leftmost)
                farthest_diff = abs(window[0] - target)
                if diff < farthest_diff:
                    window.popleft()  # Remove farthest
                    window.append(val)  # Add closer
                elif val > target:
                    break  # Ascending, all further are worse

            # Move to right subtree
            curr = curr.right

        # Step 4: Return k closest values
        return list(window)
  • Line 10-11: Create a deque to hold k values.
  • Line 13-16: Use a stack for inorder traversal, start at root.
  • Line 17-22: Push all left nodes onto stack.
  • Line 24-35: Pop node, process value:
    • If deque < k, append.
    • If full, compare diff with farthest; if closer, slide window; if farther and past target, stop.
  • Line 37: Move to right subtree.
  • Line 39: Return deque as list.
  • Time Complexity: O(n)—traverses all nodes in worst case.
  • Space Complexity: O(k + h)—deque (k) and stack (h, height).

This solution is like a smart picker—grabs k best matches on the go!

Alternative Solution: Min-Heap Approach

Section link icon

Why an Alternative Approach?

The min-heap approach collects all BST values and their differences from the target, then picks the k smallest using a heap. It’s O(n log k) time—slower but intuitive—using O(n) space, offering a clear way to see all options before optimizing with traversal.

How It Works

Think of this as gathering all stars in a heap, ranking them by distance from your target, and plucking the k nearest. Here’s how it works, step-by-step:

  • Step 1: Traverse BST, store (diff, value) pairs in a heap.
  • Step 2: Keep heap size at k by popping largest diffs.
  • Step 3: Extract k values from heap.

It’s like sorting a pile—keep the k best shining through!

Step-by-Step Example

Example: Tree: Root: 4, Left: 2 (Left: 1, Right: 3), Right: 5; Target: 3.714286; k: 2

  • Step 1: Values: [1, 2, 3, 4, 5].
  • Step 2: Heap (max-heap of diffs, k size):
    • (2.714286, 1), (1.714286, 2) → [(2.714286, 1), (1.714286, 2)].
    • (0.714286, 3): Pop (2.714286, 1), add → [(1.714286, 2), (0.714286, 3)].
    • (0.285714, 4): Pop (1.714286, 2), add → [(0.714286, 3), (0.285714, 4)].
    • (1.285714, 5): Keep → [(0.714286, 3), (0.285714, 4)].
  • Step 3: Extract: [3, 4].
  • Result: [3, 4].

Code for Min-Heap Approach

import heapq

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def closestKValues(self, root: TreeNode, target: float, k: int) -> List[int]:
        # Step 1: Collect all values with diffs in heap
        heap = []

        def inorder(node):
            if not node:
                return
            inorder(node.left)
            # Max-heap: use negative diff to pop largest
            heapq.heappush(heap, (-abs(node.val - target), node.val))
            if len(heap) > k:
                heapq.heappop(heap)
            inorder(node.right)

        inorder(root)

        # Step 2: Extract k values
        return [val for _, val in heap]
  • Time Complexity: O(n log k)—n insertions, k heap size.
  • Space Complexity: O(k)—heap size.

It’s a heap-powered picker—steady but bulkier.

Comparing the Two Solutions

Section link icon
  • Best Solution (Inorder Window):
    • Pros: O(n) time, O(k + h) space, efficient.
    • Cons: Window logic to grasp.
  • Alternative Solution (Min-Heap):
    • Pros: O(n log k) time, O(k) space, intuitive heap.
    • Cons: Slower, more memory.

Inorder wins for speed.

Additional Examples and Edge Cases

Section link icon

Single Node

  • Root: 1, Target: 2, k: 1 → [1]

All Equal Diffs

  • Root: 2, Left: 1, Right: 3, Target: 2, k: 2 → [1, 2]

Large k

  • Root: 3, Left: 1, Right: 5, Target: 4, k: 3 → [1, 3, 5]

Both handle these well.

Complexity Breakdown

Section link icon
  • Inorder Window:
    • Time: O(n)—full traversal.
    • Space: O(k + h)—deque and stack.
  • Min-Heap:
    • Time: O(n log k)—heap ops.
    • Space: O(k)—heap.

Inorder is faster.

Key Takeaways

Section link icon
  • Inorder Window: Slide through smartly.
  • Min-Heap: Heap sorts diffs.
  • BST: Order aids efficiency.
  • Python Tip: Deques flex for windows—see [Python Basics](/python/basics).

Final Thoughts: Find k Closest Like a Pro

Section link icon

LeetCode 272: Closest BST Value II in Python is a stellar BST challenge. The inorder solution glides through efficiently, while the min-heap piles up options. Want more? Try LeetCode 270: Closest BST Value or LeetCode 230: Kth Smallest Element in BST. Ready to hunt? Head to Solve LeetCode 272 on LeetCode and grab those k closest values today!