𝔩𝔢𝔩𝕠𝔭𝔢𝔷
Connect With Me on LinkedIn

Python Study

Find Closest Value in BST

Finding the Closest Value in a Binary Search Tree

Introduction

Binary search trees (BSTs) are fundamental data structures in computer science and are known for their efficient search operations. This article explores the challenge of finding the closest value to a given target in a BST, a problem that combines BST properties with numerical comparisons.

Problem Statement

Given:

  • A Binary Search Tree (BST)
  • A target integer value

Objective:

  • Return the value in the BST that is closest to the target value

Example:

  • Input: BST = [10, 5, 15, 2, 5, 13, 22, 1, 14], target = 12
  • Output: 13

Assumption:

  • There will always be only one closest value
  • Each BST node has an integer value, and left and right child nodes

Strategy and Hypothesis

We can leverage the BST property to navigate the tree efficiently. By comparing the target with each node's value, we can decide whether to move left or right, gradually narrowing down to the closest value.

Implementation

Initial Attempt

def findClosestValueInBst(tree, target):
    currentNode = tree
    solution = None
    # Write your code here.

    while solution is None:
        valueDiff = currentNode.value - target
        leftDiff = currentNode.left.value - target if currentNode.left.value is not None else None
        rightDiff = currentNode.right.value - target if currentNode.right.value is not None else None

        valueDist = abs(valueDiff)
        leftDist = abs(leftDiff)
        rightDist = abs(rightDiff)

        minDist = min([valueDist, leftDist, rightDist])

        if minDist is valueDist:
            solution = currentNode.value
        elif minDist is leftDist:
            currentNode = currentNode.left
        else:
            currentNode = currentNode.right

    return solution

# This is the class of the input tree. Do not edit.
class BST:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

Unfortunately, I didn't account for potentially closer values nested on the right side. My ternary has a mistake where I try to access the child node value without checking if the child node exists first. I was hopeful the abs method would handle None values, but that's not true. I'll have to find another way to ensure we don't go left or right when there are no child nodes.

Fixed Attempt

def findClosestValueInBst(tree, target):
    node = tree
    sol = float("inf")

    while node is not None:
        nodeDist = abs(node.value - target)
        solDist = abs(sol - target)

        sol = node.value if nodeDist < solDist else sol

        if node.value < target:
            node = node.right
        else:
            node = node.left

    return sol

# This is the class of the input tree. Do not edit.
class BST:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

Here, we initialize the solution to infinity to start using the abs function immediately without worrying about None. Also, by storing the solution, we can easily compare nested nodes without losing the solution if none of the child nodes work.

Recursive Attempt

def findClosestValueInBst(tree, target):
    return findClosestValueInBstHelper(tree, target, float("inf"))

def findClosestValueInBstHelper(node, target, solution):
    if node is None:
        return solution
    if abs(target - solution) > abs(target - node.value):
        solution = node.value
    if target < node.value:
        return findClosestValueInBstHelper(node.left, target, solution)
    if target > node.value:
        return findClosestValueInBstHelper(node.right, target, solution)

    return solution

# This is the class of the input tree. Do not edit.
class BST:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

Optimization Analysis

Optimizing for Space

Both the iterative and recursive solutions have an optimal space complexity of O(1) for the average case. The iterative solution maintains constant space usage regardless of the tree's size. While having O(log N) space complexity in the worst case due to the call stack, the recursive solution is tail-recursive and can be optimized by most modern compilers to use constant space.

Optimizing for Time

Both solutions already have optimal time complexity of O(log N) for a balanced BST, as they leverage the BST property to eliminate half of the remaining nodes at each step. In the worst case (a completely unbalanced tree), the time complexity is O(N), where N is the number of nodes in the tree.

Optimal Space & Time

The current implementations are already optimal in terms of space and time complexity for this problem. The iterative solution might have a slight edge in practical performance due to avoiding the function call overhead.

Key Takeaways

  1. BST properties allow for efficient searching, reducing time complexity from O(N) to O(log N) in balanced trees.
  2. Iterative and recursive approaches can both solve this problem efficiently.
  3. Initializing with infinity (or the root value) simplifies comparisons and handles edge cases elegantly.
  4. Tail recursion can be as space-efficient as iteration when optimized by the compiler.
  5. The problem demonstrates the importance of understanding tree traversal and comparison operations.
  6. Careful handling of null checks is crucial to avoid runtime errors in tree algorithms.

Conclusion

Finding the closest value in a BST is an excellent problem that combines binary search principles with numerical comparisons. Both iterative and recursive solutions offer optimal time complexity, with the iterative approach offering slightly better space efficiency in practice. This problem highlights the importance of leveraging data structure properties (in this case, the BST property) to design efficient algorithms. It also demonstrates how seemingly complex problems can be solved elegantly with relatively simple code when the underlying principles are well understood. As developers, mastering such fundamental algorithms and data structures is crucial for efficiently solving a wide range of more complex problems.

Previous
Branch Sums