# 95. Unique Binary Search Trees II
## Catalan Numbers
Mapping among several Catalan question:
https://hackmd.io/8apoErvuTXaUtIxxIGQTWQ#Number-of-binary-search-trees-BSTs-for-N-keys
## recursive
Using the idea of the math below
$$
C_0 = 1 \quad \text{and} \quad C_{n+1} = \sum_{i=0}^n C_i\,C_{n-i}\quad\text{for }n\ge 0
$$
```python=
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def generateTrees(self, n: int) -> List[TreeNode]:
def helper(start, end):
if start>end:
return [None] # null ptr as a terminating node
roots=[]
for i in range(start,end+1):
left_nodes=helper(start, i-1)
right_nodes=helper(i+1, end)
for l in left_nodes:
for r in right_nodes:
roots.append(TreeNode(i))
roots[-1].left=l
roots[-1].right=r
return roots
if n==0:
return []
return helper(1,n)
```
## DP, re-using/pointing to previous/smaller trees
https://leetcode.wang/leetCode-95-Unique-Binary-Search-TreesII.html#%E8%A7%A3%E6%B3%95%E4%B8%89-%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92
```python=
def generateTrees(self, n: int) -> List[TreeNode]:
if n==0:
return []
def clone_tree_and_give_offset(root_of_the_cloned, offset_to_add):
if root_of_the_cloned == None:
return None
output_node = TreeNode(root_of_the_cloned.val + offset_to_add)
output_node.left = clone_tree_and_give_offset(root_of_the_cloned.left, offset_to_add)
output_node.right = clone_tree_and_give_offset(root_of_the_cloned.right, offset_to_add)
return output_node
dp = []
for length in range(n+1): # need n+1 items
dp.append([]) # not use [[]]*10 # they refer to the same empty list
dp[0].append(None) # as the base case
for length in range(1,n+1):
for chosen_root_ind in range(1,length+1):
len_of_left_tree = chosen_root_ind - 1
len_of_right_tree = length - chosen_root_ind
for root_of_left_tree in dp[len_of_left_tree]:
for root_of_right_tree in dp[len_of_right_tree]:
root_of_all = TreeNode(chosen_root_ind)
root_of_all.left = root_of_left_tree
root_of_all.right = clone_tree_and_give_offset(root_of_right_tree, chosen_root_ind)
dp[length].append(root_of_all)
return dp[n]
```
## DP/iterative
Using the idea of the math below
$$
C_0 = 1 \quad \text{and} \quad C_{n+1}=\frac{2(2n+1)}{n+2}C_n
$$
https://leetcode.wang/leetCode-95-Unique-Binary-Search-TreesII.html#%E8%A7%A3%E6%B3%95%E5%9B%9B-%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92-2
```python=
def generateTrees(self, n: int) -> List[TreeNode]:
if n==0:
return []
def copy_a_tree(root):
if root == None:
return None
new_root = TreeNode(root.val)
new_root.left = copy_a_tree(root.left)
new_root.right = copy_a_tree(root.right)
return new_root
pre = [None]
for i in range(1,n+1):
cur = []
for root in pre:
# insert as a new root # this part is certain to happen just once
inserted_root = TreeNode(i)
inserted_root.left = root
cur.append(inserted_root)
if root != None:
# try to insert at other position, but not as a new root
steps_to_take = 0 # remember how many steps we need to go right-down
while True:
new_root = copy_a_tree(root)
focusing_node = new_root
for _ in range(steps_to_take):
focusing_node = focusing_node.right
original_right_child = focusing_node.right
inserted_node = TreeNode(i)
focusing_node.right = inserted_node
if original_right_child == None:
cur.append(new_root)
break
else:
inserted_node.left = original_right_child
cur.append(new_root)
steps_to_take += 1 # remember to go deeper at next time
pre = cur
return pre # OR, return pre # both work
```