航班晚点到凌晨起飞,在机场随缘看了下leetcode的每日一题。
感觉题目挺有意思也挺有难度的,给一个数n,找出所有可能含 n 个节点的 真二叉树 ,并以列表形式返回。答案中每棵树的每个节点都必须符合 Node.val == 0 (真二叉树 是一类二叉树,树中每个节点恰好有 0 或 2 个子节点。)
这道题我看了题目,没有思路,直接看了答案,答案有两种解题方式,第一种是通过递归,第二种方法是通过动态规划。我根据答案的思路回顾一下。
因为题目中说真二叉树 是一类二叉树,树中每个节点恰好有 0 或 2 个子节点,那么可以知道,最终二叉树的节点的个数数量一定为单数(比如一个根节点+左右子节点,或者只有一个根节点),因此不论使用递归算法还是动态规划算法,都应该现判断节点数量不为单数的情况,以及节点数量为0和节点数量为1的情况,这三种情况都可以直接返回结果。
当且仅当节点数量为大于1的单数(3、5、7、9....)时,讨论递归或者动态规划才有意义。
动态规划方法:
使用动态规划算法,先创建一个列表dp[],其中dp[n]的值代表当节点数为n时,能够生成的所有二叉树列表。
脑补一下,当n=1时,只存在一个val=0的根节点,无子节点,因此d[1]直接返回[0]
当n=3时,有根节点、一个左子树、一个右子树,但是左子树和右子树下不会继续有子树,所以dp[3]=[0,0,0]
再往下推以下,由于真二叉树下的子树也是真二叉树,因此每个节点都会保持0个子节点或者2个子节点,假设总共的节点数为n,根节点的左子树占用j个节点(j必然小于n),那么根节点右子树总共会占用n-1-j个节点。n=3时,去掉根节点占用1个节点数,左右子树节点树加起来为3-1个,其中左子树用掉1个节点数,右子树还剩下3-1-1个节点树可用,
当n=5时,除了根节点外还有4个节点数可用,那么根节点下整个左子树的节点数可以为1个或者3个
当左子树节点总数为1时,左子树只能输出dp[1]=[0],右子树的总结点数为3,而总结点数为3的右子树只有一种输出的可能,即dp[3]=[0,0,0]
当左子树总数为3时,右子树的节点数为1。和上面相反,左子树输出dp[1]=[0],右子树输出dp[3]=[0,0,0]
要找到节点数为n下所有可能的真二叉树,那么就要先遍历左子树节点数为1(dp[1])、3(dp[3])、...、n-2(dp[n-2])的情况,找到分别对应的右子树节点数为n-2(dp[n-2])、...、3(dp[3])、1(dp[1])的情况,再分别组合起来放入列表中。
因此这一段的代码表示为:
for (int i = 3; i <= n; i += 2) {
for (int j = 1; j < i; j += 2) {
for (TreeNode leftSubtree : dp[j]) {
for (TreeNode rightSubtrees : dp[i - 1 - j]) {
TreeNode root = new TreeNode(0, leftSubtree, rightSubtrees);
dp[i].add(root);
}
}
}
}
复制
将n=3,首次遍历j=1的情况带入:
for (int i = 3;) {
for (int j = 1) {
for (TreeNode leftSubtree : [0]) {
for (TreeNode rightSubtrees : [0]) {
TreeNode root = new TreeNode(0, new TreeNode[0], new TreeNode[0]);
dp[3].add(root);
}
}
}
}
复制
由于当n=3时,j最大只能为1,因此以上代码直接返回n=3的结果,即new TreeNode(0, new TreeNode[0], new TreeNode[0]) = [0,0,0]
如果n=5,则左子树的节点数j可以等于1或者3,右子树的节点数可以为3或者1,返回结果会出现多个两个数组。
开始说过,要考虑n为0、1和n为偶数时直接返回结果的情况,补充这一部分后,完整的JAVA代码如下:
/**
* 定义TreeNode类方法,leecode已提前定义好。
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/正式的解答过程如下
class Solution {
public List<TreeNode> allPossibleFBT(int n) {
if (n % 2 ==0) { return new ArrayList<TreeNode>();}
List<TreeNode>[] dp = new List[n+1]
for (int i=0;i<=n;i++){
dp[i] = new ArrayList<TreeNode>();
}
dp[1].add(new TreeNode(0))
for (int i =3; i<=n; i+=2){
for(int j =1; j<i; j+=2){
for (TreeNode left:dp[j])
{
for(TreeNode right:dp[i-1-j]){
TreeNode root = new TreeNode(0, left, right);
dp[i].add(root);
}}}}
return dp[n]}}
复制
此问题还有递归解法,但是飞机要起飞了。。。感觉递归解法更直观一些,直接放答案代码(递归我看的python的)
class Solution:
def allPossibleFBT(self, n: int) -> List[Optional[TreeNode]]:
full_binary_trees = []
if n % 2 == 0:
return full_binary_trees
if n == 1:
full_binary_trees.append(TreeNode(0))
return full_binary_trees
for i in range(1, n, 2):
left_subtrees = self.allPossibleFBT(i)
right_subtrees = self.allPossibleFBT(n - 1 - i)
for left_subtree in left_subtrees:
for right_subtree in right_subtrees:
root = TreeNode(0, left_subtree, right_subtree)
full_binary_trees.append(root)
return full_binary_trees
作者:力扣官方题解
链接:https://leetcode.cn/problems/all-possible-full-binary-trees/solutions/2713780/suo-you-ke-neng-de-zhen-er-cha-shu-by-le-1uku/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。