Skip to main content

Unique Binary Search Trees II

LeetCode 95 | Difficulty: Medium​

Medium

Problem 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.

When to use

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.

When to use

Generate all combinations/permutations, or find solutions that satisfy constraints.


Solutions​

Solution 1: C# (Best: 220 ms)​

MetricValue
Runtime220 ms
Memory28.6 MB
Date2019-12-16
Solution
/**
* 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​

ApproachTimeSpace
Dynamic Programming$O(n)$$O(n)$
Backtracking$O(n! or 2^n)$$O(n)$

Interview Tips​

Key Points
  • 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.