二叉树的题真是一节更比六节强,先补上昨天的最后一题
拿到这题,当然是一脸懵,甚至不知道根节点在哪?
不过考虑到后序遍历的特性:左右中,postorder数组的最后一个元素,一定是根节点。再到inorder数组中去找对应的左右子树范围(postorder中找每棵树的根节点,以此把前序数组分割成左右两个部分,代表两棵子树),再递归的去切分左右子树,便能构造完整的树。(草稿纸上用手模拟一遍,过程还是很清晰,但是代码不知道咋写)
具体来说,分为以下六步:
-
第一步:如果数组大小为零的话,说明是空节点了。 递归出口
-
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
-
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
-
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
-
第五步:切割后序数组,切成后序左数组和后序右数组
-
第六步:递归处理左区间和右区间
class Solution: def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]: #递归出口,当postorder的长度为零,说明构造完了 if len(postorder) == 0: return None #以下是单层处理逻辑,首先找到每颗子树的根节点 node = TreeNode(postorder[-1]) inorder_index = inorder.index(node.val) #用后序数组的根节点,到中序数组中找到左右子树对应的范围 inorder_left = inorder[:inorder_index] inorder_right = inorder[inorder_index + 1 :] '''中序数组中,左右子树范围对应的长度,应该等于后续数组中左右子树范围对应的长度 以此找到,后续数组中。左右子树所在的区间,再取后续数组的最后一个元素,又找到对应子树 的根节点,此即递归逻辑 ''' postorder_left = postorder[:len(inorder_left)] postorder_right = postorder[len(inorder_left) : len(inorder_left) + len(inorder_right)] #递归调用,构建二叉树 node.left = self.buildTree(inorder_left , postorder_left ) node.right = self.buildTree(inorder_right , postorder_right) return node
LC 654. 最大二叉树 终于自己写出来一次 ,可喜可贺!
-
class Solution: def constructMaximumBinaryTree(self, nums: List[int]) -> Optional[TreeNode]: if not nums: return M = max(nums) root = TreeNode(M) idx = nums.index(M) nums_left = nums[:idx] nums_right = nums[idx + 1 :] if len(nums_left) == 0 and len(nums_right) == 0: return root if len(nums_left) == 0: root.left = None root.left = self.constructMaximumBinaryTree(nums_left) if len(nums_right) == 0: root.right == None root.right = self.constructMaximumBinaryTree(nums_right) return root
其实做的过程中,没思考用哪种遍历方式,很自然的使用了先序。因为只有先设置根节点,才能再设置左右节点的指针。
-
实际操作中,我是一上来就把数组从最大值切分成左右两个子数组,递归出口自然就是左右两边都为空,比如[1]这种情况(卡哥的方法是直接检测数组长度,若为1则到达递归出口,本质是一样的)。由于是先切分,有可能导致后续
root.right = self.constructMaximumBinaryTree(nums_right)
这种语句中传入空列表,因此需要在开头加上判断语句,遇到空列表直接返回。
-
事实上不加判断也可以,在后面递归调用的时候加上if-else语句,避免在空列表的情况下调用函数即可,如下:
-
if len(nums_right) == 0: root.right == None else: root.right = self.constructMaximumBinaryTree(nums_right)
这和之前的区别是,之前即便是空列表,设置为了None之后,还会调用函数一次,现在空列表就不会调用了。
-
代码容易,但是要注意和前几题的区别:本题递归函数调用时有返回值!
-
为什么会有返回值?因为当检测到节点值和target相等时,我们就要把这个节点返回出来,若不用res接住返回值,func函数将无值可返(一开始想当然的写成在func最后return node,也没用res,输出的自然是整棵树)。而前面几题调用时没有返回值是因为后面还要对其进行某种操作,在最后在输出操作完成后的结果,本题是满足条件,立即输出,因此必须要当场返回
-
class Solution: def func(self,node,val): if not node or node.val == val: return node if node.val > val: res = self.func(node.left , val) if node.val < val: res = self.func(node.right , val) return res def searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: return self.func(root , val)