Unique Binary Search Trees II - Complete Solution Guide
Unique Binary Search Trees II is LeetCode problem 95, a Medium level challenge. This complete guide provides step-by-step explanations, multiple solution approaches, and optimized code in python3, java, cpp, c.
Problem Statement
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]] Constraints: 1 <= n <= 8
Detailed Explanation
The problem asks us to generate all structurally unique Binary Search Trees (BSTs) for a given number `n`. These BSTs must contain nodes with unique values from 1 to `n`. The output should be a list containing the root nodes of all possible BSTs.
Solution Approach
The provided solution uses a recursive approach with memoization (dynamic programming) to efficiently generate all unique BSTs. The core idea is to iterate through all possible root values (from `start` to `end`) and then recursively generate all possible left subtrees (from `start` to `i-1`) and right subtrees (from `i+1` to `end`). Then, combine each possible left subtree with each possible right subtree to form a new BST with root `i`. Memoization is used to store the results of already computed subproblems to avoid redundant calculations. This dramatically improves performance.
Step-by-Step Algorithm
- Step 1: Define a recursive function `build(start, end)` that takes a start and end value, representing the range of numbers to build BSTs from.
- Step 2: Base Case: If `start > end`, it means there are no numbers in the range, so return a list containing only `None` (representing an empty subtree). This signifies the end of a branch.
- Step 3: Memoization: Before computing, check if the result for `(start, end)` is already in the memo. If so, return the memoized result.
- Step 4: Iteration: Iterate through all possible root values `i` from `start` to `end`.
- Step 5: Recursive Calls: For each `i`, recursively call `build(start, i-1)` to get all possible left subtrees and `build(i+1, end)` to get all possible right subtrees.
- Step 6: Tree Construction: Iterate through all possible combinations of left and right subtrees. Create a new `TreeNode` with value `i`, set the left child to the current left subtree and the right child to the current right subtree.
- Step 7: Accumulate Results: Add the newly created tree to the result list.
- Step 8: Memoize Result: After processing all possible root values, store the result list in the memo for future use.
- Step 9: Return Result: Return the result list.
Key Insights
- Insight 1: The number of unique BSTs grows rapidly with `n`. Brute force generation of every possible tree would be inefficient.
- Insight 2: Dynamic programming with memoization is crucial. The problem can be broken down into smaller subproblems: generating all possible left subtrees and right subtrees for a given root.
- Insight 3: Recursive approach is natural for generating trees. We can iterate through each number from 1 to n, treat it as the root, recursively construct all possible left and right subtrees and combine them
Complexity Analysis
Time Complexity: O(4^n/sqrt(n))
Space Complexity: O(4^n/sqrt(n))
Topics
This problem involves: Dynamic Programming, Backtracking, Tree, Binary Search Tree, Binary Tree.
Companies
Asked at: Amazon, Apple, Bloomberg, Meta, Microsoft, Uber.