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 the value 0
.
A full binary tree is a binary tree where every node has either 0 or 2 children. You may return the answer in any order.
n
representing the number of nodes.n
nodes, where each node has value 0
.Constraints:
1 <= n <= 20
0
.
At first, you might think about generating all possible binary trees with n
nodes and then filtering out the ones that are not full. However, that approach is inefficient, as the number of binary trees grows rapidly with n
.
Instead, we can use the definition of a full binary tree to build only valid trees:
n
is even, there are no possible full binary trees.
We can recursively construct all trees by:
We use a recursive, memoized approach to generate all full binary trees:
n == 1
, the only possible tree is a single node. Return a list containing a single-node tree.
n
is even, return an empty list (since full binary trees can only have an odd number of nodes).
left_nodes
from 1 to n-2
(step 2), do:right_nodes = n - 1 - left_nodes
.left_nodes
and right_nodes
nodes.n
so that we do not recompute the same result multiple times.
n
.
This approach ensures that:
Let's walk through the case where n = 5
:
n
is odd and greater than 1, we try all possible splits:
left_nodes
and right_nodes
:
left_nodes = 1
, only one tree: single node.
right_nodes = 3
:
1|1
(since 3-1=2, only odd split is 1 and 1).left_nodes = 3
and right_nodes = 1
, swap the above.
Brute-force Approach:
n
(super-exponential, as the number of binary trees grows as Catalan numbers).n
nodes is the (n-1)/2
th Catalan number, which grows exponentially but much slower than the number of all binary trees.
C_n
), where C_n
is the Catalan number for n
nodes.
C_n
) for storing all trees and the memoization cache.
For n <= 20
, this approach is efficient and practical.
To generate all possible full binary trees with n
nodes:
n
(since full binary trees have an odd number of nodes).from typing import List, Optional
# Definition for a binary tree node.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def allPossibleFBT(self, n: int) -> List[Optional[TreeNode]]:
memo = {}
def build(n):
if n in memo:
return memo[n]
if n == 1:
return [TreeNode(0)]
if n % 2 == 0:
return []
res = []
for left_nodes in range(1, n, 2):
right_nodes = n - 1 - left_nodes
for left in build(left_nodes):
for right in build(right_nodes):
root = TreeNode(0)
root.left = left
root.right = right
res.append(root)
memo[n] = res
return res
return build(n)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
unordered_map<int, vector<TreeNode*>> memo;
vector<TreeNode*> allPossibleFBT(int n) {
if (memo.count(n)) return memo[n];
vector<TreeNode*> res;
if (n == 1) {
res.push_back(new TreeNode(0));
return res;
}
if (n % 2 == 0) return res;
for (int left_nodes = 1; left_nodes < n; left_nodes += 2) {
int right_nodes = n - 1 - left_nodes;
vector<TreeNode*> left_trees = allPossibleFBT(left_nodes);
vector<TreeNode*> right_trees = allPossibleFBT(right_nodes);
for (auto left : left_trees) {
for (auto right : right_trees) {
TreeNode* root = new TreeNode(0);
root->left = left;
root->right = right;
res.push_back(root);
}
}
}
memo[n] = res;
return res;
}
};
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() { val = 0; }
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
import java.util.*;
class Solution {
private Map<Integer, List<TreeNode>> memo = new HashMap<>();
public List<TreeNode> allPossibleFBT(int n) {
if (memo.containsKey(n)) return memo.get(n);
List<TreeNode> res = new ArrayList<>();
if (n == 1) {
res.add(new TreeNode(0));
return res;
}
if (n % 2 == 0) return res;
for (int left_nodes = 1; left_nodes < n; left_nodes += 2) {
int right_nodes = n - 1 - left_nodes;
List<TreeNode> left_trees = allPossibleFBT(left_nodes);
List<TreeNode> right_trees = allPossibleFBT(right_nodes);
for (TreeNode left : left_trees) {
for (TreeNode right : right_trees) {
TreeNode root = new TreeNode(0);
root.left = left;
root.right = right;
res.add(root);
}
}
}
memo.put(n, res);
return res;
}
}
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {number} n
* @return {TreeNode[]}
*/
var allPossibleFBT = function(n) {
const memo = {};
function build(n) {
if (memo[n]) return memo[n];
if (n === 1) return [new TreeNode(0)];
if (n % 2 === 0) return [];
let res = [];
for (let left_nodes = 1; left_nodes < n; left_nodes += 2) {
let right_nodes = n - 1 - left_nodes;
let left_trees = build(left_nodes);
let right_trees = build(right_nodes);
for (let left of left_trees) {
for (let right of right_trees) {
let root = new TreeNode(0);
root.left = left;
root.right = right;
res.push(root);
}
}
}
memo[n] = res;
return res;
}
return build(n);
};