Unique Binary Search Trees II
LeetCode 95 | Difficulty: Mediumβ
MediumProblem Descriptionβ
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`
Topics: Dynamic Programming, Backtracking, Tree, Binary Search Tree, Binary Tree
Approachβ
Dynamic Programmingβ
Break the problem into overlapping subproblems. Define a state (what information do you need?), a recurrence (how does state[i] depend on smaller states?), and a base case. Consider both top-down (memoization) and bottom-up (tabulation) approaches.
Optimal substructure + overlapping subproblems (counting ways, min/max cost, feasibility).
Backtrackingβ
Explore all candidates by building solutions incrementally. At each step, choose an option, explore further, then unchoose (backtrack) to try the next option. Prune branches that can't lead to valid solutions.
Generate all combinations/permutations, or find solutions that satisfy constraints.
Solutionsβ
Solution 1: C# (Best: 220 ms)β
| Metric | Value |
|---|---|
| Runtime | 220 ms |
| Memory | 28.6 MB |
| Date | 2019-12-16 |
/**
* Definition for a binary tree node.
* public class TreeNode {
* public int val;
* public TreeNode left;
* public TreeNode right;
* public TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public IList<TreeNode> GenerateTrees(int n) {
if(n==0) return new List<TreeNode>();
return generateTrees(1, n);
}
public List<TreeNode> generateTrees(int start, int end)
{
List<TreeNode> trees = new List<TreeNode>();
if (start > end)
{
trees.Add(null);
return trees;
}
for (int rootValue = start; rootValue <= end; rootValue++)
{
List<TreeNode> leftSubTrees = generateTrees(start, rootValue - 1);
List<TreeNode> rightSubTrees = generateTrees(rootValue + 1, end);
foreach (TreeNode leftSubTree in leftSubTrees)
{
foreach (TreeNode rightSubTree in rightSubTrees)
{
TreeNode root = new TreeNode(rootValue);
root.left = leftSubTree;
root.right = rightSubTree;
trees.Add(root);
}
}
}
return trees;
}
}
Complexity Analysisβ
| Approach | Time | Space |
|---|---|---|
| Dynamic Programming | $O(n)$ | $O(n)$ |
| Backtracking | $O(n! or 2^n)$ | $O(n)$ |
Interview Tipsβ
- Discuss the brute force approach first, then optimize. Explain your thought process.
- Define the DP state clearly. Ask: "What is the minimum information I need to make a decision at each step?"
- Consider if you can reduce space by only keeping the last row/few values.
- Consider: "What information do I need from each subtree?" β this defines your recursive return value.
- Identify pruning conditions early to avoid exploring invalid branches.