Given an integer n
, return all the structurally unique BST's (binary search trees), which has exactly n
nodes of unique values from 1
to n
. Return the answer in any order.
Example 1:
Input: n = 3
Output: [[1,null,2,null,3],[1,null,3,2],[2,1,3],[3,1,null,null,2],[3,2,null,1]]
Example 2:
Input: n = 1
Output: [[1]]
Consider these edge cases:
n
is 0?n
increases? What are the limitations?Write a function to solve the problem efficiently.
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 brute force approach to generating unique binary search trees involves creating every single possible tree shape with the given numbers. We explore every combination of numbers as the root and then recursively do the same for the left and right subtrees. We keep all the unique tree structures we create.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, value=0, left=None, right=None):
self.value = value
self.left = left
self.right = right
def generate_unique_bsts(number):
if number == 0:
return []
def generate_trees(start, end):
if start > end:
return [None]
all_trees = []
# Iterate through all possible root nodes
for root_value in range(start, end + 1):
# Generate all possible left subtrees
left_subtrees = generate_trees(start, root_value - 1)
# Generate all possible right subtrees
right_subtrees = generate_trees(root_value + 1, end)
# Combine each left and right subtree
for left_subtree in left_subtrees:
for right_subtree in right_subtrees:
root = TreeNode(value=root_value)
root.left = left_subtree
root.right = right_subtree
all_trees.append(root)
return all_trees
# Build BSTs from 1 to number
return generate_trees(1, number)
The problem asks us to generate all possible binary search trees for a given range of numbers. Instead of building each tree individually, we use recursion to break the problem into smaller subproblems and cleverly combine their results. This avoids redundant calculations and builds the trees systematically.
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 generate_trees(number):
def generate_subtree(start_value, end_value):
if start_value > end_value:
return [None]
all_trees = []
# Iterate through each number to be the root.
for index in range(start_value, end_value + 1):
# Recursively generate all left subtrees.
all_left_subtrees = generate_subtree(start_value, index - 1)
# Recursively generate all right subtrees.
all_right_subtrees = generate_subtree(index + 1, end_value)
# Combine each left and right subtree.
for left_subtree in all_left_subtrees:
for right_subtree in all_right_subtrees:
root = TreeNode(index)
root.left = left_subtree
root.right = right_subtree
all_trees.append(root)
return all_trees
# Invoke the helper function to generate all trees.
return generate_subtree(1, number)
Case | How to Handle |
---|---|
Input n is 0 | Return a list containing only 'null' to represent an empty tree or an empty list to represent no trees. |
Input n is 1 | Return a list containing a single TreeNode with value 1. |
Input n is a large value (e.g., > 8) | The number of possible trees grows exponentially, potentially leading to stack overflow or excessive memory usage; memoization is crucial for efficiency. |
Negative Input | Input n must be positive, so return an empty list or throw an exception. |
Integer overflow with large n | The number of possible trees can become very large; the solution should avoid calculations that might lead to integer overflow. |
Recursion depth for large n | The recursive nature of the solution might lead to stack overflow errors for larger 'n'; memoization helps mitigate this by avoiding recomputation of the same subproblems. |
Memory constraints with large n | Storing all possible BSTs in a list can consume a significant amount of memory, so optimize the solution to minimize memory usage. |
Correctness of tree structure | Ensure generated trees adhere to BST properties: left child <= parent < right child for all nodes. |