代码随想录训练营 Day16打卡 二叉树 part04 513. 找树左下角的值 112. 路径总和 106. 从中序与后序遍历序列构造二叉树

代码随想录训练营 Day16打卡 二叉树 part04

一、 力扣513. 找树左下角的值

给定一个二叉树,判断它是否是 平衡二叉树
给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。
示例 :
输入: root = [2,1,3]
输出: 1

思路分析

  1. 确定递归函数的参数和返回值:

    参数:需要一个当前节点 root 和一个整数 depth 表示当前节点所在的深度。
    返回值:不需要返回值,因为我们只需要更新全局变量即可。

  2. 确定终止条件:

    当节点为空时,递归结束。
    当遇到叶子节点时,需要更新最大深度和对应的节点值。

  3. 确定单层递归的逻辑:

    更新最大深度和对应的节点值。
    递归遍历左右子树,并增加深度。

版本一 递归法 + 回溯

实现思路:使用递归+回溯方法遍历整棵树,通过比较深度找出最底层的最左边节点的值。递归中包括深度的增减来确保正确计算每个节点的深度。

class Solution:
    def findBottomLeftValue(self, root: TreeNode) -> int:
        self.max_depth = float('-inf')  # 初始化最大深度为负无穷大
        self.result = None  # 存放结果
        self.traversal(root, 0)  # 从根节点开始遍历
        return self.result  # 返回最底层最左边的节点值
    
    def traversal(self, node, depth):
        if not node.left and not node.right:
            # 如果是叶子节点
            if depth > self.max_depth:
                # 如果当前深度大于已记录的最大深度
                self.max_depth = depth  # 更新最大深度
                self.result = node.val  # 更新结果为当前叶子节点的值
            return
        
        if node.left:
            depth += 1  # 增加深度
            self.traversal(node.left, depth)  # 遍历左子树
            depth -= 1  # 回溯,恢复深度
        if node.right:
            depth += 1  # 增加深度
            self.traversal(node.right, depth)  # 遍历右子树
            depth -= 1  # 回溯,恢复深度

版本二 递归法+精简

实现思路:类似版本一,但是简化了代码,去除了不必要的深度回溯操作。直接在递归调用时增加深度,更为简洁。

class Solution:
    def findBottomLeftValue(self, root: TreeNode) -> int:
        self.max_depth = float('-inf')  # 初始化最大深度为负无穷大
        self.result = None  # 存放结果
        self.traversal(root, 0)  # 从根节点开始遍历
        return self.result  # 返回最底层最左边的节点值
    
    def traversal(self, node, depth):
        if not node.left and not node.right:
            # 如果是叶子节点
            if depth > self.max_depth:
                # 如果当前深度大于已记录的最大深度
                self.max_depth = depth  # 更新最大深度
                self.result = node.val  # 更新结果为当前叶子节点的值
            return
        
        if node.left:
            self.traversal(node.left, depth+1)  # 递归遍历左子树,直接增加深度
        if node.right:
            self.traversal(node.right, depth+1)  # 递归遍历右子树,直接增加深度

版本三 迭代法

实现思路:使用迭代的方式进行层序遍历(广度优先搜索),利用队列存储每一层的节点。记录每层的第一个节点的值,队列完成遍历后,最后记录的值即为最底层最左边的节点值。

from collections import deque
class Solution:
    def findBottomLeftValue(self, root):
        if root is None:
            return 0  # 如果根节点为空,则返回0
        queue = deque()  # 使用队列进行广度优先搜索
        queue.append(root)  # 加入根节点
        result = 0  # 初始化结果
        while queue:
            size = len(queue)  # 当前层的节点数
            for i in range(size):
                node = queue.popleft()  # 取出队列前端节点
                if i == 0:
                    result = node.val  # 记录每层的第一个节点值
                if node.left:
                    queue.append(node.left)  # 左子节点入队
                if node.right:
                    queue.append(node.right)  # 右子节点入队
        return result  # 循环结束时,result即为最底层最左边的节点值

力扣题目链接
题目文章讲解
题目视频讲解

二、 力扣112. 路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。
示例 :
在这里插入图片描述
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

图中可以看出,遍历的路线,并不要遍历整棵树,所以递归函数需要返回值,可以用bool类型表示。
在这里插入图片描述

版本一 递归法

实现思路:这个版本通过递归遍历树的所有路径,并使用回溯来恢复计数状态。对于每个节点,若其为叶子节点且当前计数为0,则找到了有效路径。

class Solution:
    def traversal(self, cur: TreeNode, count: int) -> bool:
        if not cur.left and not cur.right and count == 0:  # 如果当前节点是叶子节点且计数为0
            return True  # 找到符合条件的路径
        if not cur.left and not cur.right:  # 如果是叶子节点但计数不为0
            return False  # 当前路径不符合条件
        
        if cur.left:  # 如果存在左子节点
            count -= cur.left.val  # 减去左子节点的值
            if self.traversal(cur.left, count):  # 递归检查左子树
                return True  # 如果找到符合条件的路径,返回True
            count += cur.left.val  # 回溯,恢复原来的计数值
            
        if cur.right:  # 如果存在右子节点
            count -= cur.right.val  # 减去右子节点的值
            if self.traversal(cur.right, count):  # 递归检查右子树
                return True  # 如果找到符合条件的路径,返回True
            count += cur.right.val  # 回溯,恢复原来的计数值
            
        return False  # 如果左右子树都没有找到符合条件的路径,返回False
    
    def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        if root is None:
            return False  # 如果根节点为空,直接返回False
        return self.traversal(root, sum - root.val)  # 从根节点开始遍历,初始化计数为sum减去根节点的值

版本二 递归 + 精简

实现思路:这个版本简化了递归函数,去除了显式的回溯操作,直接在递归调用中更新sum。检查每个叶子节点时,如果剩余的sum等于节点的值,说明找到了一条有效路径。

class Solution:
    def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        if not root:
            return False  # 如果节点为空,返回False
        if not root.left and not root.right and sum == root.val:
            return True  # 如果是叶子节点且剩余的sum等于节点值,返回True
        # 递归检查左右子树,更新sum为sum减去当前节点的值
        return self.hasPathSum(root.left, sum - root.val) or self.hasPathSum(root.right, sum - root.val)

版本三 迭代法

实现思路:使用栈来迭代地遍历树,检查每个叶子节点的路径和是否等于sum。这种方法不需要递归调用,使用显示的栈来保存每个节点的状态。

class Solution:
    def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        if not root:
            return False  # 如果根节点为空,返回False
        st = [(root, root.val)]  # 使用栈,初始化包含根节点和其值
        while st:
            node, path_sum = st.pop()  # 弹出一个元素
            # 检查是否为叶子节点且路径和等于sum
            if not node.left and not node.right and path_sum == sum:
                return True  # 如果符合条件,返回True
            # 将子节点压栈,并更新路径和
            if node.right:
                st.append((node.right, path_sum + node.right.val))
            if node.left:
                st.append((node.left, path_sum + node.left.val))
        return False  # 如果栈为空也没有找到有效路径,返回False

力扣题目链接
题目文章讲解
题目视频讲解

三、 力扣106. 从中序与后序遍历序列构造二叉树

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
示例 :
在这里插入图片描述
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

首先回忆一下如何根据两个顺序构造一个唯一的二叉树,相信理论知识大家应该都清楚,就是以 后序数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来再切后序数组。一层一层切下去,每次后序数组最后一个元素就是节点元素。
流程如图:

在这里插入图片描述
说到一层一层切割,就应该想到了递归。

来看一下一共分几步:

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

代码实现

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        # 特殊情况处理: 如果前序遍历结果为空,则二叉树也必然为空
        if not preorder:
            return None

        # 前序遍历的第一个元素为当前的根节点
        root_val = preorder[0]
        root = TreeNode(root_val)  # 创建根节点

        # 在中序遍历中找到根节点的位置,这个位置将数组分为左右两部分
        separator_idx = inorder.index(root_val)

        # 根据中序遍历中根节点的位置,切割中序数组,得到左子树和右子树的中序遍历结果
        inorder_left = inorder[:separator_idx]
        inorder_right = inorder[separator_idx + 1:]

        # 根据中序遍历中左子树的节点数量,切割前序数组,得到左子树和右子树的前序遍历结果
        # 前序遍历的左子树的长度与中序遍历的左子树长度相同
        preorder_left = preorder[1:1 + len(inorder_left)]
        preorder_right = preorder[1 + len(inorder_left):]

        # 递归构建左子树和右子树
        root.left = self.buildTree(preorder_left, inorder_left)
        root.right = self.buildTree(preorder_right, inorder_right)

        # 返回构建好的树的根节点
        return root

力扣题目链接
题目文章讲解
题目视频讲解

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值