研习代码 day16 | 递归+回溯 && 两个遍历序列构造二叉树

一、找树左下角的值

        1.1 题目

        给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

        假设二叉树中至少有一个节点。

示例 1:

输入: root = [2,1,3]
输出: 1

示例 2:

输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7

提示:

  • 二叉树的节点个数的范围是 [1,10^4]
  • -2^31 <= Node.val <= 2^31 - 1 

        1.2 题目链接

        513.找树左下角的值

        1.3 解题思路和过程想法

        (1)解题思路

        层序遍历:利用层序遍历找到最后一层的第一个元素
        递归遍历+回溯:利用递归找到左起第一个叶子节点(不论利用哪种递归都可以实现,因为左指针永远出现在右指针的前面,并且不用对根节点做任何操作),过程中用 level 记录当前层数,并利用回溯及时修改当前指针的 level,若遇到叶子节点则比较其层数与 maxLevel 的大小,若是更深层次的左叶子,则修改 maxLevel 和 res 。

        (2)过程想法

        利用层序遍历来找树左下角的值是比较简单的,也很好想;
        递归遍历+回溯:其实也不难想,主要是要理解主体部分的逻辑和回溯,递归和回溯其实是分不开的,递归到一定深度并碰到出口之后就会向上回溯,此处只不过是需要及时修改一下回溯时用于记录层数的 level 变量,便于同等处理其他分支节点。

        1.4 代码

        1.4.1 层序遍历
from queue import Queue

class Solution:
    def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
        # 利用层序遍历找到最后一层的第一个元素

        myQueue = Queue()

        if root:
            myQueue.put(root)

        while not myQueue.empty():
            level = []
            size = myQueue.qsize()
            for i in range(size):
                cur = myQueue.get()
                level.append(cur.val) 
                if cur.left:
                    myQueue.put(cur.left)
                if cur.right:
                    myQueue.put(cur.right)

        return level[0]
        1.4.2 递归遍历+回溯
class Solution:
    def find(self,root,level):
        # 递归出口
        if not root.left and not root.right:
            if level > self.maxLevel:
                self.maxLevel = level
                self.res = root.val
            return 
        
        # 左
        # 因为出口处没有判断root.left是否成立,所以此处进行判断
        if root.left:
            level += 1
            self.find(root.left,level)
            # 回溯
            level -= 1
        
        # 右
        # 因为出口处没有判断root.right是否成立,所以此处进行判断
        if root.right:
            level += 1
            self.find(root.right,level)
            # 回溯
            level -= 1

        # 根:因为不需对根做操作,所以此处并未有对根的描述

    
    def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
        # 前中后序遍历都可以,因为到最后都是左右的关系
        self.res = root.val
        self.maxLevel = 1

        self.find(root,1)

        return self.res

二、路径求和

        2.1 题目

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:

  1. 创建一个根节点,其值为 nums 中的最大值。
  2. 递归地在最大值 左边 的 子数组前缀上 构建左子树。
  3. 递归地在最大值 右边 的 子数组后缀上 构建右子树。

返回 nums 构建的 最大二叉树 

示例 1:

输入:nums = [3,2,1,6,0,5]
输出:[6,3,5,null,2,0,null,null,1]
解释:递归调用如下所示:
- [3,2,1,6,0,5] 中的最大值是 6 ,左边部分是 [3,2,1] ,右边部分是 [0,5] 。
    - [3,2,1] 中的最大值是 3 ,左边部分是 [] ,右边部分是 [2,1] 。
        - 空数组,无子节点。
        - [2,1] 中的最大值是 2 ,左边部分是 [] ,右边部分是 [1] 。
            - 空数组,无子节点。
            - 只有一个元素,所以子节点是一个值为 1 的节点。
    - [0,5] 中的最大值是 5 ,左边部分是 [0] ,右边部分是 [] 。
        - 只有一个元素,所以子节点是一个值为 0 的节点。
        - 空数组,无子节点。

示例 2:

输入:nums = [3,2,1]
输出:[3,null,2,null,1]

提示:

  • 1 <= nums.length <= 1000
  • 0 <= nums[i] <= 1000
  • nums 中的所有整数 互不相同

        2.2 题目链接

        654.最大二叉树

        2.3 解题思路和过程想法

        (1)解题思路

        前序遍历+回溯:

        # 利用前序遍历,在过程中提前累加非空节点的值,一但找到符合要求的叶子节点,则更新结果self.res
        # 若不符合,则在回溯时减去刚刚不符合要求节点的值以更新 summ 记录当前分支情况

        (2)过程想法

        因为已经写过类似”递归+回溯“的题了,所以思考的过程比较顺畅;回溯部分的代码是可以精简的,但因为是刚接触不久,所以写全了。

        2.4 代码

class Solution:
    def pathSum(self,root,summ):
        # 利用前序遍历+回溯

        # 递归出口:空节点和叶子节点
        if not root:
            return 

        if not root.left and not root.right:
            if self.targetSum == summ:
                self.res = True
            return

        # 左
        if root.left:
            summ += root.left.val
            self.pathSum(root.left,summ)
            summ -= root.left.val

        # 右
        if root.right:
            summ += root.right.val
            self.pathSum(root.right,summ)
            summ -= root.right.val

    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        # 利用前序遍历,在过程中提前累加非空节点的值,一但找到符合要求的叶子节点,则更新结果self.res
        # 若不符合,则在回溯时减去刚刚不符合要求节点的值以更新 summ 记录当前分支情况
        self.res = False
        self.targetSum = targetSum

        if root:
            self.pathSum(root,root.val)
            return self.res
        else:
            return False

三、从中序与后序遍历序列构造二叉树

        3.1 题目

        给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

示例 1:

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

示例 2:

输入:inorder = [-1], postorder = [-1]
输出:[-1]

提示:

  • 1 <= inorder.length <= 3000
  • postorder.length == inorder.length
  • -3000 <= inorder[i], postorder[i] <= 3000
  • inorder 和 postorder 都由 不同 的值组成
  • postorder 中每一个值都在 inorder 中
  • inorder 保证是树的中序遍历
  • postorder 保证是树的后序遍历

        3.2 题目链接

        106.从中序与后序遍历序列构造二叉树

        3.3 解题思路和过程想法        

        (1)解题思路:

        后序数组的最后节点——根节点,中序数组中:根节点左侧是左子树,根节点右侧是右子树
        # 第一步:如果数组大小为零的话,说明是空节点了。
        # 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
        # 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
        # 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
        # 第五步:切割后序数组,切成后序左数组和后序右数组 (中序数组和后序数组的长度一定是相等的)
        # 第六步:递归处理左区间和右区间

        (2)过程想法:

        之前每次写完都记不住,有思路但是每次都手足无措,希望这次写完之后能记住。

        3.4 代码

class Solution:
    def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:                                  
        # 第一步:如果数组大小为零的话,说明是空节点了。
        if not postorder:
            return None

        # 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
        root_val = postorder[-1]
        root = TreeNode(root_val)

        # 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
        seperate_idex = inorder.index(root_val)

        # 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
        inorder_left = inorder[:seperate_idex]
        inorder_right = inorder[seperate_idex+1:]

        # 第五步:切割后序数组,切成后序左数组和后序右数组 (中序数组和后序数组的长度一定是相等的)
        postorder_left = postorder[:len(inorder_left)]
        postorder_right = postorder[len(inorder_left):len(postorder)-1]

        # 第六步:递归处理左区间和右区间
        root.left = self.buildTree(inorder_left,postorder_left)
        root.right = self.buildTree(inorder_right,postorder_right)

        return root
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值