DAY16 二叉树part4

513. 找树左下角的值

中等

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

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

读题便知用层序简单,所以这道题更适合用迭代。

❌向左遍历到最后一个(不一定是最底层的)

在树的最后一行找到最左边的值(不一定是左孩子)

✅判断最后一行:深度为最大值的叶子结点

✅判断最左边:优先左的遍历都可以(本题不需要处理中节点,所以前中后遍历都可)

递归 

递归三部曲

  1. 参数和返回值:root;两个全局变量,maxLen用来记录最大深度,result记录最大深度最左节点的数值。
  2. 终止条件:遇到叶子结点时,更新最大深度(⚠️何时返回result)
  3. 单层遍历逻辑:左/右孩子存在,则深度加一并遍历和回退

⚠️:traversal函数做的是当遇到叶子结点时,更新两个全局变量的值

回溯发生在递归函数返回时,即当一个递归调用结束后,控制权会返回到其调用者(上一个节点)。

# 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 findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
        self.max_len = -inf
        self.result = root.val
        self.traversal(root, 0) # 更新self.result 和 self.max_depth
        return self.result

    def traversal(self, root, depth):
        if root.left is None and root.right is None:
            if depth > self.max_len:
                self.max_len = depth
                self.result = root.val
            return
# 找到叶子节点时需要更新 self.result并return
# 但不应该在 traversal 方法中直接返回 self.result,因为这样会导致在遍历过程中提早结束。

        # 左
        if root.left:
            # depth+=1
            # self.traversal(root.left,depth)
            # depth-=1 # 回退
            self.traversal(root.left,depth+1) # depth+1没有改变depth的值,隐藏回溯
        # 右
        if root.right:
            self.traversal(root.right,depth+1) 
# traversal 方法不需要返回任何值,因为它的主要作用是更新 self.result 和 self.max_depth。
l_height = get_height(node.left)
r_height = get_height(node.right)
return max(l_height,r_height)+1 # 求高度

(其实在文章中110用的是中序遍历求高度),求深度应该用前序

if node.left: # 左孩子存在
    self.getDepth(node.left, depth + 1) #遍历更新结果并回溯
if node.right:
    self.getDepth(node.right, depth + 1)

 迭代(队列实现层序)

✅先把儿子入队,再把儿子入队,这样最后一个出队的节点就是左下角的节点了。 

# 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 findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
        q = deque([root])
        while q:
            node = q.popleft()
            if node.right: q.append(node.right)
            if node.left: q.append(node.left)
        return node.val

112. 路径总和

简单

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

叶子节点 是指没有子节点的节点。

递归三部曲:

1、参数和返回值:

        root;本题我们要找一条符合条件的路径,所以递归函数需要返回值bool类型

2、确定终止条件:

        递减,遇到叶子结点判断targetnum == 0

3、确定单层递归的逻辑

  • 递归地检查左子树和右子树,看看是否在其中一条路径上存在满足条件的路径。
  • 使用逻辑或 (or) 运算符,意味着只要任意一条路径满足条件,整个方法就会返回 True
# 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 hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if root is None:
            return False
        targetSum -= root.val
        if root.left is None and root.right is None:
            return targetSum == 0     
        return self.hasPathSum(root.left,targetSum) or self.hasPathSum(root.right,targetSum)

113. 路径总和 II

中等

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

叶子节点 是指没有子节点的节点。

 

不是很理解pop()的时机

# 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 pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
        path = []
        result = []
        self.findpath(root, targetSum, path, result)
        return result
    def findpath(self, root, targetSum, path, result):
        if root is None:
            return []
        path.append(root.val) # 遍历时加入当前节点值
        targetSum -= root.val # 递减
        if root.left is None and root.right is None and targetSum == 0:
            result.append(path.copy()) # 遍历到叶子结点且符合路径总和,path加入result
        self.findpath(root.left, targetSum, path, result) # 向左递归遍历
        self.findpath(root.right, targetSum, path, result)
        path.pop()

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

中等

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

  1. 后序序列最后一个节点为根节点
  2. 切中序数组:在中序数组中找到根节点作为切割点,分出左、右子树
  3. 切后序数组
  4. 递归处理左区间、后区间

方法

class Solution:
    def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
        if not postorder:  # 空节点
            return None
        left_size = inorder.index(postorder[-1])  # 左子树的大小
        left = self.buildTree(inorder[:left_size], postorder[:left_size])
        right = self.buildTree(inorder[left_size + 1:], postorder[left_size: -1])
        return TreeNode(postorder[-1], left, right

存在的问题:

  • list.index()方法的时间复杂度是 O(n),因为它需要遍历整个列表来查找元素.
  • 如果 postorder[-1] 不在 inorder 列表中,调用 inorder.index() 会抛出 ValueError。需要先确认元素存在。
  • inorder[:left_size]:这会创建一个新的列表,包含 inorder 列表中从索引 0left_size - 1 的元素。这是一个复制操作,因为它会生成一个新列表。

所以算法复杂度为{O(n^2){}}(时间+空间)

优化

  1. 用一个哈希表(或者数组)预处理 inorder 每个元素的下标,这样就可以 O(1) 查到 postorder[n−1] 在 inorder 的位置,从而 O(1) 知道左子树的大小。
  2. 把递归参数改成子数组下标区间(左闭右开区间)的左右端点,从而避免复制数组。
# 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 buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
        index = {x: i for i, x in enumerate(inorder)} 
        # x = inorder[i]
        def dfs(in_l: int, in_r: int, post_l: int, post_r: int):
            if post_l == post_r: 
                # 后序的左边界和右边界相同 没子树 
                return None
            left_size = index[postorder[post_r - 1]] - in_l
            left = dfs(in_l, in_l + left_size, post_l, post_l + left_size)
            right = dfs(in_l + 1 + left_size, in_r, post_l + left_size, post_r - 1)
            return TreeNode(postorder[post_r - 1], left, right)
        return dfs(0, len(inorder), 0, len(postorder)) 

时间复杂度:O(n). 空间复杂度:O(n) 

105. 从前序与中序遍历序列构造二叉树 

中等

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

# 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 buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
        index = {x: i for i, x in enumerate(inorder)}
        # 哈希表,将中序遍历的每个值与其索引对应起来
        def dfs(pre_l: int, pre_r: int, in_l: int, in_r:int) -> Optional[TreeNode]:
            if pre_l == pre_r:
                return None
            left_size = index[preorder[pre_l]] - in_l 
            # index[preorder[pre_l]]是根节点的值在字典中的索引-in_l也是索引
            left = dfs(pre_l + 1, pre_l + 1 + left_size, in_l, in_l + left_size) 
            # 左闭右开 pre+1跳过根节点
            right = dfs(pre_l + 1 + left_size, pre_r, in_l + 1 + left_size, in_r)
            return TreeNode(preorder[pre_l], left, right) 
"""            
            创建一个新节点,并将其左右子树链接在一起。递归地创建每个节点,并把它们连接到二叉树的正确位置

            假设我们正在构建上面提到的例子,
            当前递归层正是构建根节点 20
            当调用 return TreeNode(preorder[2], left, right) 时
            preorder[2] 返回 20。left 是 TreeNode(15)(左子树)。
            right 是 TreeNode(7)(右子树)
"""
        return dfs(0, len(preorder), 0, len(inorder))

 ⚠️ 前序和后序无法构造树

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值