Given the root
of a binary search tree, and an integer k
, return the kth
smallest value (1-indexed) of all the values of the nodes in the tree.
For example:
Consider the following binary search tree:
3
/ \
1 4
\
2
If k = 1
, the 1st smallest value is 1
.
If k = 2
, the 2nd smallest value is 2
.
If k = 3
, the 3rd smallest value is 3
.
If k = 4
, the 4th smallest value is 4
.
As another example:
5
/ \
3 6
/ \
2 4
/
1
If k = 3
, the 3rd smallest value is 3
.
Constraints:
n
.1 <= k <= n <= 10^4
0 <= Node.val <= 10^4
Follow up: If the BST is modified often (i.e., we can do insert and delete operations) and you need to find the kth smallest frequently, how would you optimize?
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:
The goal is to find the Kth smallest element in a special tree structure. The brute force strategy involves examining every element in the tree to determine which one is the Kth smallest.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def kth_smallest_element_brute_force(root, k):
all_elements = []
def inorder_traversal(node):
if node:
inorder_traversal(node.left)
all_elements.append(node.val)
inorder_traversal(node.right)
# Collect all elements from the BST using inorder traversal.
inorder_traversal(root)
# Sorting ensures elements are ordered smallest to largest.
all_elements.sort()
# Return the kth smallest element, adjusting for 0-based indexing.
return all_elements[k - 1]
The best way to find the kth smallest element in a binary search tree is to take advantage of its ordered nature. We can walk through the tree in a special order that lets us count the elements as we go, stopping as soon as we reach the kth one. This avoids looking at parts of the tree that don't matter.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def kth_smallest(root, k_value):
stack = []
current_node = root
nodes_visited_count = 0
while current_node or stack:
# Traverse to the leftmost node.
while current_node:
stack.append(current_node)
current_node = current_node.left
current_node = stack.pop()
# Increment count; it's the next smallest.
nodes_visited_count += 1
# Found the kth smallest element.
if nodes_visited_count == k_value:
return current_node.value
# Move to the right subtree.
current_node = current_node.right
return None
Case | How to Handle |
---|---|
Empty BST (root is null) | Return null or a predefined error value (e.g., -1) if the BST is empty as there is no kth smallest element. |
k is less than 1 | Throw an IllegalArgumentException or return a special value indicating invalid input, since k must be a positive integer. |
k is greater than the number of nodes in the BST | Return null or a predefined error value (e.g., -1) as the kth smallest element does not exist. |
BST with all identical values | The in-order traversal will correctly identify the kth smallest element regardless of identical values. |
BST with negative and positive values | The in-order traversal algorithm works correctly with both negative and positive values as it only relies on the ordering property of the BST. |
BST with very large or very small values that could lead to integer overflow | Use `long` data type where applicable to avoid potential integer overflow issues during calculations related to node values or number of nodes. |
Extremely unbalanced BST (skewed tree) | The iterative inorder traversal approach should still work, but for a very skewed tree the space complexity might reach O(n) in the worst case instead of O(h), with h being the height of the tree. |
k is equal to 1 (find the minimum value) | The algorithm should correctly return the leftmost node of the BST when k=1. |