Given an array of integers preorder, which represents the preorder traversal of a BST (binary search tree), check if it is a valid preorder traversal sequence.
Assume that the values of the BST are distinct.
Example 1:
Input: preorder = [5,2,1,3,6]
Output: true
Example 2:
Input: preorder = [5,2,6,1,3]
Output: false
Constraints:
1 <= preorder.length <= 1040 <= preorder[i] <= 104preorder are unique.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 basic idea is to explore all possible binary search trees that can be constructed from the given sequence. We check if any of these trees satisfy the preorder property. If even one such tree exists, we know the sequence is a valid preorder traversal for some binary search tree.
Here's how the algorithm would work step-by-step:
def verify_preorder_brute_force(preorder):
def is_valid_preorder(sequence):
if not sequence:
return True
root_value = sequence[0]
# Iterate through all possible split points
for split_index in range(1, len(sequence) + 1):
left_subtree = sequence[1:split_index]
right_subtree = sequence[split_index:]
# Check if all values in the left subtree are smaller than the root
is_left_subtree_valid = all(value < root_value for value in left_subtree)
# Check if all values in the right subtree are greater than the root
is_right_subtree_valid = all(value > root_value for value in right_subtree)
# Recursively check if left and right subtrees are valid
if is_left_subtree_valid and is_right_subtree_valid:
# Check if both subtrees are valid BSTs based on preorder traversal
if is_valid_preorder(left_subtree) and is_valid_preorder(right_subtree):
return True
return False
return is_valid_preorder(preorder)The key idea is to maintain a lower bound, representing the smallest value that any right subtree node can have. We traverse the sequence, checking if each element is greater than the lower bound; if it is, it could be a valid node in the Binary Search Tree (BST).
Here's how the algorithm would work step-by-step:
def verify_preorder(preorder):
lower_bound = float('-inf')
stack_of_smaller_nodes = []
for node_value in preorder:
# If the current value is less than the lower bound, it's invalid.
if node_value < lower_bound:
return False
# Pop nodes from the stack until we find a larger value.
while stack_of_smaller_nodes and node_value > stack_of_smaller_nodes[-1]:
# This node is the parent of the right subtree, so set lower bound.
lower_bound = stack_of_smaller_nodes.pop()
# Push the current node onto the stack.
stack_of_smaller_nodes.append(node_value)
return True| Case | How to Handle |
|---|---|
| Empty input array | Return true, as an empty tree is a valid BST and its preorder traversal is an empty sequence. |
| Single element array | Return true, as a single node is a valid BST. |
| Array with two elements, valid increasing order | Handle the case where the second element is greater than the first, representing a right child. |
| Array with two elements, invalid decreasing order | Handle the case where the second element is less than the first, representing a left child. |
| Array with numbers in strictly decreasing order | The input violates the BST property, so the solution should return false after comparing against lower bound. |
| Array with numbers in strictly increasing order | Represents a right-skewed BST, should be handled correctly by the algorithm and return true. |
| Large input array exceeding memory or stack limits with recursion | Iterative solutions are preferred to prevent stack overflow errors with large datasets; a stack data structure can efficiently store the lower bound. |
| Integer overflow potential when comparing large values | Use appropriate data types (long) or consider using a relative comparison to avoid overflow when comparing the values against the lower bound. |