You are given the root
of a binary search tree (BST) and an integer val
.
Find the node in the BST whose value equals val
and return the subtree rooted with that node. If such a node does not exist, return null
.
Example 1:
Consider the following BST:
4
/ \
2 7
/ \
1 3
If val = 2
, the expected output is the subtree rooted at node 2
:
2
/ \
1 3
Example 2:
Using the same BST:
4
/ \
2 7
/ \
1 3
If val = 5
, the expected output is null
because a node with value 5 does not exist in the BST.
Describe an algorithm to efficiently find the node with value val
in the BST. Analyze the time and space complexity of your solution. Discuss any edge cases that need to be handled.
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
Let's say you're looking for a specific book in a library organized like a family tree. The most basic way is to simply check every single book until you find the one you want. This is essentially what a brute force approach does.
Here's how the algorithm would work step-by-step:
def search_in_binary_search_tree_brute_force(root, target):
if root is None:
return None
nodes_to_visit = [root]
while nodes_to_visit:
current_node = nodes_to_visit.pop(0)
if current_node.val == target:
return current_node
# Add left child to the queue if it exists
if current_node.left:
nodes_to_visit.append(current_node.left)
# Add right child to the queue if it exists
if current_node.right:
nodes_to_visit.append(current_node.right)
# If the target is not found after visiting all nodes.
return None
The efficient way to search a special tree is to use its structure to guide you directly to the target. Each step eliminates a large portion of the tree, making the search very fast. This approach focuses on navigating directly towards the target like a guided missile.
Here's how the algorithm would work step-by-step:
def search_bst(root, target_value):
current_node = root
while current_node:
# If target is smaller, go left
if target_value < current_node.val:
current_node = current_node.left
# If target is larger, go right
elif target_value > current_node.val:
current_node = current_node.right
# Value found!
else:
return current_node
return None
Case | How to Handle |
---|---|
Empty tree (root is null) | Return null immediately as the target cannot be found in an empty tree. |
Target value is smaller than the smallest value in the BST | The algorithm should traverse to the leftmost leaf and terminate correctly when the target is not found. |
Target value is larger than the largest value in the BST | The algorithm should traverse to the rightmost leaf and terminate correctly when the target is not found. |
BST with only one node and target matches the node's value | The algorithm should return the single node if its value matches the target. |
BST with only one node and target does not match the node's value | The algorithm should return null if the single node's value does not match the target. |
Target value is present multiple times in the BST (duplicates) | The algorithm should return the first node encountered during traversal with the target value, as BST search typically only finds the first matching node. |
Extremely skewed BST (essentially a linked list) | The algorithm's time complexity degrades to O(n) in the worst case (skewed tree), which must be acknowledged, but traversal still works correctly. |
Integer overflow when comparing large target value or node values | While unlikely with typical integer ranges, consider using long if potential overflow is a concern, especially with given node values. |