Problem
Given an integer n
, return a list of all possible full binary trees with n
nodes. Each node of each tree in the answer must have Node.val == 0
.
Each element of the answer is the root node of one possible tree. You may return the final list of trees in any order.
A full binary tree is a binary tree where each node has exactly 0
or 2
children.
Examples
Example 1:
graph TD %% Tree 1 subgraph T1[Tree 1] A1[0] --> B1[0] & C1[0] C1[0] --> D1[0] & E1[0] E1[0] --> F1[0] & G1[0] end %% Tree 2 subgraph T2[Tree 2] A2[0] --> B2[0] & C2[0] C2[0] --> D2[0] & E2[0] D2[0] --> F2[0] & G2[0] end %% Tree 3 subgraph T3[Tree 3] A3[0] --> B3[0] & C3[0] B3[0] --> D3[0] & E3[0] C3[0] --> F3[0] & G3[0] end
graph TD; %% Tree 4 subgraph T4[Tree 4] A4[0] --> B4[0] & C4[0] B4[0] --> D4[0] & E4[0] E4[0] --> F4[0] & G4[0] end %% Tree 5 subgraph T5[Tree 5] A5[0] --> B5[0] & C5[0] B5[0] --> D5[0] & E5[0] D5[0] --> F5[0] & G5[0] end
Input: n = 7
Output: [[0,0,0,null,null,0,0,null,null,0,0],[0,0,0,null,null,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,null,null,null,null,0,0],[0,0,0,0,0,null,null,0,0]]
Example 2:
Input: n = 3
Output: [[0,0,0]]
Solution
Method 1 - Recursion
- Base Case and Initial Check:
- A full binary tree cannot have an even number of nodes. This is because a full binary tree either has 0 or 2 children for every node. Hence, for any given
n
that is even, it is impossible to create such a tree. For example, withn = 2
nodes, you cannot form a valid tree since one node would be left without a pair. - If
n
is even, we return an empty list as there are no possible full binary trees.
- A full binary tree cannot have an even number of nodes. This is because a full binary tree either has 0 or 2 children for every node. Hence, for any given
- Recursive Build and Memoization:
- For an odd number
n
, we compute possible full binary trees by recursively finding potential subtrees at each level. - For every
i
(wherei
is the number of nodes in the left subtree), we computeallPossibleFBT(i)
for the left subtree andallPossibleFBT(n - 1 - i)
for the right subtree (considering the root makes one node). - Combining all possible left and right subtrees forms the full binary trees for the current node count
n
. - Memoization is employed to store results for each computed
i
to avoid recomputation and enhance efficiency.
- For an odd number
Code
Java
class Solution {
public List<TreeNode> allPossibleFBT(int n) {
if (n % 2 == 0) {
return new ArrayList<>();
}
return allPossibleFBTUtil(n, new HashMap<>());
}
private List<TreeNode> allPossibleFBTUtil(int n, Map<Integer, List<TreeNode>> memo) {
if (n == 1) {
return Arrays.asList(new TreeNode(0));
}
if (memo.containsKey(n)) {
return memo.get(n);
}
List<TreeNode> ans = new ArrayList<>();
for (int i = 1; i < n; i += 2) {
List<TreeNode> leftTrees = allPossibleFBTUtil(i, memo);
List<TreeNode> rightTrees = allPossibleFBTUtil(n - 1 - i, memo);
for (TreeNode left : leftTrees) {
for (TreeNode right : rightTrees) {
TreeNode root = new TreeNode(0);
root.left = left;
root.right = right;
ans.add(root);
}
}
}
memo.put(n, ans);
return ans;
}
}
Python
class TreeNode:
def __init__(self, x: int):
self.val: int = x
self.left: 'TreeNode' = None
self.right: 'TreeNode' = None
class Solution:
def allPossibleFBT(self, n: int) -> List[TreeNode]:
if n % 2 == 0:
return []
return self._allPossibleFBT(n, {})
def _allPossibleFBT(self, n: int, memo: Dict[int, List[TreeNode]]) -> List[TreeNode]:
if n == 1:
return [TreeNode(0)]
if n in memo:
return memo[n]
ans: List[TreeNode] = []
for i in range(1, n, 2):
left_trees = self._allPossibleFBT(i, memo)
right_trees = self._allPossibleFBT(n - 1 - i, memo)
for left in left_trees:
for right in right_trees:
root = TreeNode(0)
root.left = left
root.right = right
ans.append(root)
memo[n] = ans
return ans
Complexity
- ⏰ Time complexity:
O(2^n)
, because each recursive call generates exponential subproblems. - 🧺 Space complexity:
O(2^n)
for storing all trees which also requires exponential space.