给你一个整数 n
,请你找出所有可能含 n
个节点的 真二叉树 ,并以列表形式返回。答案中每棵树的每个节点都必须符合 Node.val == 0
。
答案的每个元素都是一棵真二叉树的根节点。你可以按 任意顺序 返回最终的真二叉树列表。
真二叉树 是一类二叉树,树中每个节点恰好有 0
或 2
个子节点。
示例 1:
输入:n = 7 输出:[[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]]
示例 2:
输入:n = 3 输出:[[0,0,0]]
提示:
1 <= n <= 20
官方题解参考:
# 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]]:
res=[]
if n==1:
return [TreeNode(0)]
if n%2==0:
return []
left_num=1
right_num=n-2
while right_num>0:
left_Tree=self.allPossibleFBT(left_num)
right_Tree=self.allPossibleFBT(right_num)
for i in range(len(left_Tree)):
for j in range(len(right_Tree)):
root=TreeNode(0)
root.left=left_Tree[i]
root.right=right_Tree[j]
res.append(root)
left_num+=2
right_num-=2
return res
该题对应的官方思路链接:https://leetcode.cn/problems/all-possible-full-binary-trees/solutions/21012/man-er-cha-shu-di-gui-xiang-jie-by-jalan/
如果你要构造一颗有 N 个节点的二叉树,你会怎么做?
首先,你肯定会先 new 一个根结点对象 root,然后为它构造左子树,再为它构造右子树。
那么对它的左子树 root.left 而言,它同样需要构造左子树和右子树。右子树 root.right 亦然。
因此,你的所有子树都是一棵满二叉树。
「给你一个整数 N,构造出一棵包含 N 个节点的满二叉树」。这句话是题目本身,也是无数个被拆分出的子问题。
满二叉树如何构造?
满二叉树是一类二叉树,其中每个结点恰好有 0 或 2 个子结点。
如果你要为某节点分配一个左节点,那么一定也要为它分配一个右节点。因此,如果 N 为偶数,那一定无法构成一棵满二叉树。
为了列出所有满二叉树的排列,我们可以为左子树分配 x 节点,为右子树分配 N - 1 - x(其中减 1 减去的是根节点)节点,然后递归地构造左右子树。
x 的数目从 1 开始,每次循环递增数目 2(多增加 2 个节点,等于多增加 1 层)。
递归过程
递归最关心的两个问题是:
结束条件
自身调用
对于这个问题来说,结束条件为:
当 N 为偶数时:无法构造满二叉树,返回空数组
当 N == 1 时:树只有一个节点,直接返回包含这个节点的数组
当完成 N 个节点满二叉树构造时:返回结果数组
个人思路:
1、二叉树问题对我来说一直比较困难,因此该问题拿到后思路并不是很清晰,所以参考了其它lcer的答案。
2、思路卡点在穷举出所有的真二叉树、递归的子问题和上阶问题的连接。子问题的确认(根节点为子问题还是叶子节点为子问题)。
分析:
思路不清晰的原因:寻找子问题过程中犯迷糊了,错误的子问题思路如下~
1、如果叶子节点为子问题,那么该树的创建为从上至下,先创建好了根节点和其左右子树,再创建子树对应的子子树,循环结束条件为递归传参为0时,认为达到了叶子节点的终点,再将子问题依次回写回去,从而递归结束,进行输出,生成了一颗真二叉树。
2、如果根节点为子问题,那么该树的创建流就为从下至上,先创建好了叶子节点和其上阶,再慢慢的创建中间节点,直到创建根节点为止,循环的结束条件为递归的传参为N时,子问题为根节点,再将根节点和叶子节点进行回写,进行输出,生成一颗真二叉树。(该思路在想的过程中就发现很凌乱,因为创建一棵树正常来说我们是第一眼就能先创建好了根节点和其左右子树,然后才会进行左右子树依次进行之后的生成,类似链表的流程一样,需要先创建链表头,而不是直接创建链表尾。因此正确的创建树的流程为先创建根节点,而叶子节点为其子问题,因为只有叶子节点创建好了后,才能依次的怼回到对应的非叶子节点中。
正确的树的子问题思路如下:
首先根节点不是该问题的最终问题,叶子节点也不是该问题的子问题,因为根节点和叶子节点只是该树的节点,分布在不同的层级而已,她们之间没有递进关系,只是整个平铺树的不同层级位置。
那么该问题的最终问题是创建一整颗真二叉树,而子问题为凑成一颗真二叉树的每一个子二叉树。一个子二叉树必须具备一个根节点和其左右子节点,由这些重复的子二叉树凑成一颗真二叉树。而对于每一个子二叉树,其左右子节点又是由其下阶的子二叉树构成,依次往下走,直到走到叶子节点时生成了一个独立的子二叉树,此时递归结束。
因此假设需要生成一个为N的真二叉树:
步骤一:生成一个构造二叉树的方法gTree(n),n为该树的总节点个数,假设n为3,该gTree(3)构造的子树如下:
步骤二:生成根节点的真二叉树,该真二叉树的节点个数为n,采用步骤一的构造方法,该方法中首先生成一个val为0的根节点,同时对左右子树的节点数进行分配,分别为left_num、right_num,其中n=left_num+right_num+1
步骤三:真二叉树的类型有非常多种,由left_num和right_num的组合决定。假设是一个节点数为7的真二叉树,那么相对于根节点来说它的左右子树的组合方式就有:[1,5]、[3,3]、[5,1]这三种组合,gTree(1)为一个0节点。
步骤四:针对于步骤三的案例,可以发现规律真二叉树在第二层级的左子树节点数量为依次加2,也可以说成是由右子树的节点数量依次减2组成,依次类推,下阶的子树也同理。
步骤五:因此该真二叉树的建立分为2个模块,模块一、穷举出左右子树的节点数量可能的组合,该组合用一个步长为2的循环就可以做到;模块二、上阶的gTree(n)的左右子树由下阶的gTree(left_num)和gTree(right_num)组成,其中left_num+right_num+1=n,因此该模块属于一个递归问题。
步骤六:将生成的真二叉树加入容器中进行返回。