代码随想录算法训练营第十四天(二叉树篇)|Leetcode513找树左下角的值,Leetcode112路径总和,Leetcode106从中序和后序遍历构造二叉树

Leetcode 513 找树左下角的值

题目链接: 找树左下角的值

给定一棵二叉树的根节点,返回该二叉树中最后一行最靠左的节点的值。

思路: 本题既可以通过迭代求解,也可以通过递归求解。

若通过迭代法求解,则直接通过层序遍历,遍历整棵二叉树。最后将最后一行的第一个元素取出即可。

若使用递归法求解,则需要注意递归顺序。此题中我们采用前序遍历,遍历整棵二叉树,同时我们维护一个最大深度变量。若当前节点深度大于最大深度时,更新最大深度的同时记录该节点信息。由于我们采用的是前序遍历(中左右),当深度突破最大深度时,记录的相应节点信息一定是最靠左的节点。

利用递归三部曲进行分析:

  1. 递归函数参数包括当前正在遍历的节点和当前节点的深度; 返回值为空,因为我们采用一个全局变量记录节点最大深度
  2. 当前节点为空时,终止递归操作
  3. 每层递归操作中,首先比较当前节点深度与最大深度,据此更新最左子节点。随后,递归地处理左右孩子节点

具体代码实现

# 迭代法
# 通过层序遍历,取最后一层的最左侧节点,直接返回即可
class Solution:
    def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
        from collections import deque

        result = []
        if root is None:
            return 0

        myQueue = deque()
        myQueue.append(root)

        while myQueue:
            size = len(myQueue)
            floorResult = []

            for _ in range(size):
                node = myQueue.popleft()
                floorResult.append(node.val)

                if node.left:
                    myQueue.append(node.left)
                if node.right:
                    myQueue.append(node.right)

            result.append(floorResult)

        return result[-1][0]
# 递归法
class Solution:
    maxDepth = 0
    result = 0
    def traverse(self, cur: Optional[TreeNode], depth: int) -> None:
        if cur is None:
            return

        # 更新最大深度
        if depth > self.maxDepth:
            self.maxDepth = depth
            self.result = cur.val

        # 递归处理左右节点。
        # 需要特别注意的是在递归过程中包含深度的回溯,而回溯的过程包含在参数中。
        # 此处传递的是 `depth + 1` 而非 `depth`
        # 此操作与 depth += 1, self.traverse(cur.left, depth), depth -=1 等价
        self.traverse(cur.left, depth + 1)
        self.traverse(cur.right, depth + 1)

    def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
        self.traverse(root, 1)
        return self.result

Leetcode 112 路径总和

题目链接: 路径总和

给定一棵二叉树的根节点以及一个整数。判断该二叉树中是否存在一条从根节点到叶节点的路径,使得该路径上所有节点值相加等于给定的整数。

思路: 此题涉及到路径,需要我们进行回溯操作。

利用递归三部曲进行分析:

  1. 递归函数的参数与返回值
    递归函数参数为当前正在遍历的节点,沿着当前路径已经累加得到的和以及目标和; 返回值为布尔值,含义为是否存在一条路径符合要求。只要有一条路径符合要求,则说明当前二叉树符合要求。
  2. 递归终止条件
    当遍历到空节点时,停止向下递归,比较沿着当前路径的和与目标和是否相等,若相等,则返回True,否则返回False
  3. 每层递归所做操作
    每层递归时将自己的左右孩子节点加入到路径中(注意回溯)

具体代码实现

class Solution:
    def getPath(self, cur: Optional[TreeNode], sum: int, targetSum: int) -> bool:

        # 当前节点为叶子节点,构成了一条完整的路径
        # 判断该路径总和是否与目标和相等
        if cur.left is None and cur.right is None:
            if sum == targetSum:
                return True
            return False

        # 汇总包含左子节点的路径的情况和包含右子节点路径的情况
        # 注意回溯过程隐藏在参数传递的过程中
        if cur.left and cur.right:
            return self.getPath(cur.left, sum+cur.left.val, targetSum) or self.getPath(cur.right, sum+cur.right.val, targetSum)

        # 仅存在左子节点
        if cur.left:
            return self.getPath(cur.left, sum+cur.left.val, targetSum)

        # 仅存在右子节点
        if cur.right:
            return self.getPath(cur.right, sum+cur.right.val, targetSum)

    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if root is None:
            return False
        return self.getPath(root, root.val, targetSum)

Leetcode 106 从中序遍历和后续遍历构造二叉树

题目链接: 从中序遍历和后续遍历构造二叉树

给定中序遍历数组和后序遍历数组,需要据此构造一个相应的二叉树,并返回相应的根节点。给定的数组中确保不存在值相同的元素

Example1:

inorder = [9,3,15,20,7]
postorder = [9,15,7,20,3]
能够唯一确定以下二叉树:

    3
   / \
  9  20
    /  \
   15   7

思路: 首先回忆中序遍历和后序遍历的特质。中序遍历通过左中右的方式进行遍历操作,后序遍历通过左右中的方式进行遍历操作。

我们首先根据后序遍历数组确定二叉树根节点,再以此划分中序数组,将其划分为左子树和右子树两个部分。由于中序遍历和后序遍历对应的元素个数相同。因此我们可以通过中序数组划分后两个部分的长度,确定新的后序数组。重复以上操作,直到完成所有节点的构建。

举例来说,整个过程如下:
inorder = [9,3,15,20,7]
postorder = [9,15,7,20,3]

根据后序数组,确定二叉树根节点为 3,以 3 为基准,划分中序数组。中序数组被划分为了两部分: [9], [15,20,7],这两部分的长度分别为 1和3。再据此划分后序数组,同样划分为两部分: [9], [15,7,20]。以此类推,重复以上步骤。

利用递归三部曲进行分析:

  1. 递归函数参数与返回值:
    递归函数参数应当为中序遍历数组和后序遍历数组,返回值应当为创建好的二叉树相应节点。
  2. 递归终止条件:
    当后序遍历数组为空时,递归结束,返回空节点。
  3. 每层递归所做的操作:
    根据后序遍历确定根节点,根据根节点在中序数组中的下标分割中序数组,随后再分割后序数组

具体代码实现

根据分割数组实现方式的不同,可以分为复制数组片段和传递原数组以及相应下标两种实现方式

class Solution:
    def build(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
        # 后序数组为空,终止递归操作
        if not postorder:
            return

        rootVal = postorder.pop()
        rootNode = TreeNode(rootVal)

        # 找出该元素在中序数组中的下标
        for i, num in enumerate(inorder):
            if num == rootVal:
                index = i
                break

        # 创建新数组对象,占用更多内存资源
        leftInorder = inorder[:index]
        rightInorder = inorder[index+1:]

        leftPostorder = postorder[:index]
        rightPostorder = postorder[index:]

        rootNode.left = self.build(leftInorder, leftPostorder)
        rootNode.right = self.build(rightInorder, rightPostorder)

        return rootNode

    def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
        root = self.build(inorder, postorder)
        return root
class Solution:
    def build(self, inorder: List[int], postorder: List[int], inorderStartIdx: int, inorderEndIdx: int, postorderStartIdx: int, postorderEndIdx: int) -> Optional[TreeNode]:
        if postorderStartIdx >= postorderEndIdx:
            return None

        rootVal = postorder[postorderEndIdx-1]
        rootNode = TreeNode(rootVal)

        for i, num in enumerate(inorder):
            if num == rootVal:
                index = i
                break

        leftInorderStartIdx = inorderStartIdx
        leftInorderEndIdx = index

        rightInorderStartIdx = index + 1
        rightInorderEndIdx = inorderEndIdx

        leftPostorderStartIdx = postorderStartIdx
        leftPostorderEndIdx = postorderStartIdx + index - inorderStartIdx

        rightPostorderStartIdx = postorderStartIdx + index - inorderStartIdx
        rightPostorderEndIdx = postorderEndIdx - 1

        # 传递相应数组下标,这样递归函数中不需要再创建新的数组,节约了内存空间
        rootNode.left = self.build(inorder, postorder, leftInorderStartIdx, leftInorderEndIdx, leftPostorderStartIdx, leftPostorderEndIdx)
        rootNode.right = self.build(inorder, postorder, rightInorderStartIdx, rightInorderEndIdx, rightPostorderStartIdx, rightPostorderEndIdx)

        return rootNode


    def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
        # 坚持在划分区间时采用左闭右开区间
        root = self.build(inorder, postorder, 0, len(inorder), 0, len(postorder))
        return root
posted @ 2025-08-22 20:10  雪痕春风天音九重色  阅读(2)  评论(0)    收藏  举报