代码随想录算法训练营Day 18 || 513.找树左下角的值、112. 路径总和、113. 路径总和ii、106.从中序与后序遍历序列构造二叉树、105.从前序与中序遍历序列构造二叉树

513.找树左下角的值

力扣题目链接

给定一个二叉树,在树的最后一行找到最左边的值。

迭代法: 

from collections import deque
class Solution:
    def findBottomLeftValue(self, root):
        if root is None:
            return 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

解释:

from collections import deque

这行代码导入了Python标准库中的deque模块,它提供了双向队列数据结构的实现。

class Solution:
    def findBottomLeftValue(self, root):

这部分定义了一个名为Solution的类,其中包含了一个名为findBottomLeftValue的方法。这个方法接受一个root参数,表示二叉树的根节点。

    if root is None:
        return 0

这个条件判断语句检查根节点是否为None,如果是,则直接返回0。

    queue = deque()
    queue.append(root)
    result = 0

这部分代码创建了一个空的双向队列queue,并将根节点添加到队列中。同时,还定义了一个变量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)

这个循环迭代地处理队列中的节点。首先,获取当前队列的长度size,以便在内部循环中逐个处理每个节点。然后,通过queue.popleft()从队列的左侧弹出一个节点,并将其存储在变量node中。

在循环中,如果i等于0(即当前节点是该层的第一个节点),则将node.val赋值给result,以便在循环结束后返回最底层最左边节点的值。

接下来,如果节点的左子节点存在,则将其添加到队列中。同样地,如果节点的右子节点存在,则也将其添加到队列中。

循环将一直进行,直到队列中没有节点,即已经处理完二叉树的所有节点。

return result

最后,该方法返回存储在result中的结果值。也就是最底层最左边节点的值。

这段代码的目的是找到二叉树中最底层最左边的节点的值,并将其作为结果返回。它使用了队列这个数据结构来实现广度优先搜索(BFS)的方式来遍历二叉树。

迭代法:

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

这段代码是一个定义了一个名为`Solution`的类,其中包含了`findBottomLeftValue`和`traversal`两个方法。

`findBottomLeftValue`方法接受一个名为`root`的参数,表示树的根节点。该方法的作用是找到树的最底层最左边的节点的值。它首先初始化了`max_depth`为负无穷大,并且将`result`初始化为`None`。然后调用`traversal`方法来遍历树,并记录下最底层最左边的节点的值。最后返回`result`。

`traversal`方法接受两个参数,一个是`node`表示当前节点,另一个是`depth`表示当前节点的深度。它首先判断当前节点是否为叶子节点(即没有左右子节点),如果是,则比较当前节点的深度和`max_depth`,如果当前节点的深度大于`max_depth`,则更新`max_depth`为当前节点的深度,并将`result`设置为当前节点的值。然后返回。

如果当前节点有左子节点,那么先将`depth`加一,然后递归调用`traversal`方法来遍历左子节点,并在方法返回后将`depth`减一,恢复原来的深度。同理,如果当前节点有右子节点,也是如此处理。

这样,通过递归的方式遍历整棵树,找到最底层最左边的节点的值,并将结果保存在`result`中返回。

112. 路径总和

力扣题目链接(opens new window)

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。

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: # 遇到叶子节点直接返回
            return False
        
        if cur.left: # 左
            count -= cur.left.val
            if self.traversal(cur.left, count): # 递归,处理节点
                return True
            count += cur.left.val # 回溯,撤销处理结果
            
        if cur.right: # 右
            count -= cur.right.val
            if self.traversal(cur.right, count): # 递归,处理节点
                return True
            count += cur.right.val # 回溯,撤销处理结果
            
        return False
    
    def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        if root is None:
            return False
        return self.traversal(root, sum - root.val)

  1. class Solution: 这一行定义了一个名为 Solution 的类,这是你的解决方案的主体。

  2. def traversal(self, cur: TreeNode, count: int) -> bool: 这是一个名为 traversal 的方法,它接收两个参数:cur(当前节点)和 count(当前路径的总和)。这个方法返回一个布尔值,表示是否存在一条从当前节点到叶子节点的路径,使得路径上的节点值之和等于 count

  3. if not cur.left and not cur.right and count == 0: 这一行检查当前节点是否是叶子节点(即没有左右子节点),并且 count 是否为0。如果是,那么就找到了一条满足条件的路径,返回 True

  4. if not cur.left and not cur.right: 这一行再次检查当前节点是否是叶子节点。如果是,但 count 不为0,那么就没有找到满足条件的路径,返回 False

  5. 接下来的几行代码分别处理当前节点的左子节点和右子节点。如果存在左子节点或右子节点,就从 count 中减去该子节点的值,然后递归调用 traversal 方法。如果递归调用返回 True,那么就找到了一条满足条件的路径,返回 True。否则,就撤销之前的操作(即将子节点的值加回到 count 中),然后处理另一个子节点。

  6. 如果遍历完所有的子节点都没有找到满足条件的路径,那么返回 False

  7. def hasPathSum(self, root: TreeNode, sum: int) -> bool: 这是一个名为 hasPathSum 的方法,它接收两个参数:root(根节点)和 sum(目标总和)。这个方法返回一个布尔值,表示是否存在一条从根节点到叶子节点的路径,使得路径上的节点值之和等于 sum

  8. 首先,检查根节点是否为空。如果为空,则直接返回 False

  9. 然后,调用 traversal 方法来查找是否存在满足条件的路径。注意这里传入的 count 值是 sum - root.val,因为我们已经包含了根节点的值。

113. 路径总和ii

力扣题目链接(opens new window)

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

class Solution:
    def __init__(self):
        self.result = []
        self.path = []

    def traversal(self, cur, count):
        if not cur.left and not cur.right and count == 0: # 遇到了叶子节点且找到了和为sum的路径
            self.result.append(self.path[:])
            return

        if not cur.left and not cur.right: # 遇到叶子节点而没有找到合适的边,直接返回
            return

        if cur.left: # 左 (空节点不遍历)
            self.path.append(cur.left.val)
            count -= cur.left.val
            self.traversal(cur.left, count) # 递归
            count += cur.left.val # 回溯
            self.path.pop() # 回溯

        if cur.right: #  右 (空节点不遍历)
            self.path.append(cur.right.val) 
            count -= cur.right.val
            self.traversal(cur.right, count) # 递归
            count += cur.right.val # 回溯
            self.path.pop() # 回溯

        return

    def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
        self.result.clear()
        self.path.clear()
        if not root:
            return self.result
        self.path.append(root.val) # 把根节点放进路径
        self.traversal(root, sum - root.val)
        return self.result 

这段Python代码的目的是找到二叉树中所有从根到叶子的路径,它们的和等于给定的目标值。代码使用了一个叫做Solution的类,它有三个属性:result, path, 和 traversal。result属性是一个列表,用来存储符合条件的最终路径。path属性是一个列表,用来存储从根节点到某个节点的当前路径。traversal属性是一个方法,它接受两个参数:cur和count。cur是当前节点,count是剩余要匹配的和。 traversal方法使用递归和回溯来遍历树中所有可能的路径。它有四个基本情况:

•  如果当前节点是叶子节点(没有左右子节点)并且count为零,说明路径达到了目标和,所以把path列表的一个副本添加到result列表中,并返回。

•  如果当前节点是叶子节点并且count不为零,说明路径没有匹配目标和,所以什么都不做,直接返回。

•  如果当前节点没有左子节点,跳过左子树,只遍历右子树。

•  如果当前节点没有右子节点,跳过右子树,只遍历左子树。 对于每次递归调用,方法执行以下步骤:

•  把当前节点的子节点(左或右)的值添加到path列表中,并从count中减去它。

•  用当前节点的子节点和更新后的count作为参数调用自己。

•  把当前节点的子节点的值加回到count中,并从path列表中弹出。这是为了撤销之前的改变,并恢复原始状态以便探索其他路径。 pathSum方法是主函数,它接受两个参数:root和sum。root是二叉树的根节点,sum是要匹配的目标值。方法初始化result和path列表为空列表,并检查root是否为None。如果是,就返回一个空的result列表。否则,就把根节点的值添加到path列表中,并用根节点和sum减去根节点的值作为参数调用traversal方法。最后,返回result列表。 

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

力扣题目链接(opens new window)

根据一棵树的中序遍历与后序遍历构造二叉树。

注意: 你可以假设树中没有重复的元素。

例如,给出

  • 中序遍历 inorder = [9,3,15,20,7]
  • 后序遍历 postorder = [9,15,7,20,3] 返回如下的二叉树:

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

class Solution:
    def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
        # 第一步: 特殊情况讨论: 树为空. (递归终止条件)
        if not postorder:
            return None

        # 第二步: 后序遍历的最后一个就是当前的中间节点.
        root_val = postorder[-1]
        root = TreeNode(root_val)

        # 第三步: 找切割点.
        separator_idx = inorder.index(root_val)

        # 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
        inorder_left = inorder[:separator_idx]
        inorder_right = inorder[separator_idx + 1:]

        # 第五步: 切割postorder数组. 得到postorder数组的左,右半边.
        # ⭐️ 重点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

这段Python代码的目的是根据中序遍历和后序遍历的结果重建二叉树。代码使用了一个叫做Solution的类,它有一个方法:buildTree。buildTree方法接受两个参数:inorder和postorder,分别是二叉树的中序遍历和后序遍历的列表。方法返回一个TreeNode对象,表示重建后的二叉树的根节点。

buildTree方法使用递归的思想来实现,它有七个步骤:

• 第一步:特殊情况讨论:树为空。(递归终止条件)如果postorder列表为空,说明没有节点,直接返回None。

• 第二步:后序遍历的最后一个元素就是当前的根节点。从postorder列表中取出最后一个元素作为根节点的值,并创建一个TreeNode对象。

• 第三步:找切割点。在inorder列表中找到根节点的值所在的位置,这个位置就是切割点,它把inorder列表分成了左子树和右子树的部分。

• 第四步:切割inorder列表。得到inorder列表的左半边和右半边,分别表示左子树和右子树的中序遍历结果。

• 第五步:切割postorder列表。得到postorder列表的左半边和右半边,分别表示左子树和右子树的后序遍历结果。注意,这里要保证切割后的列表大小跟inorder列表切割后的大小一致,所以要去掉postorder列表最后一个元素(即根节点)。

• 第六步:递归。用inorder列表和postorder列表的左半边作为参数调用buildTree方法,得到左子树的根节点,并赋值给当前根节点的左孩子。用inorder列表和postorder列表的右半边作为参数调用buildTree方法,得到右子树的根节点,并赋值给当前根节点的右孩子。

• 第七步:返回答案。返回当前根节点。

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

力扣题目链接(opens new window)

根据一棵树的前序遍历与中序遍历构造二叉树。

注意: 你可以假设树中没有重复的元素。

例如,给出

前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3,15,20,7] 返回如下的二叉树:

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

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数组. 得到inorder数组的左,右半边.
        inorder_left = inorder[:separator_idx]
        inorder_right = inorder[separator_idx + 1:]

        # 第五步: 切割preorder数组. 得到preorder数组的左,右半边.
        # ⭐️ 重点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

这段Python代码的目的是根据前序遍历和中序遍历的结果重建二叉树。代码使用了一个叫做Solution的类,它有一个方法:buildTree。buildTree方法接受两个参数:preorder和inorder,分别是二叉树的前序遍历和中序遍历的列表。方法返回一个TreeNode对象,表示重建后的二叉树的根节点。

buildTree方法使用递归的思想来实现,它有七个步骤:

• 第一步:特殊情况讨论:树为空。(递归终止条件)如果preorder列表为空,说明没有节点,直接返回None。

• 第二步:前序遍历的第一个元素就是当前的根节点。从preorder列表中取出第一个元素作为根节点的值,并创建一个TreeNode对象。

• 第三步:找切割点。在inorder列表中找到根节点的值所在的位置,这个位置就是切割点,它把inorder列表分成了左子树和右子树的部分。

• 第四步:切割inorder列表。得到inorder列表的左半边和右半边,分别表示左子树和右子树的中序遍历结果。

• 第五步:切割preorder列表。得到preorder列表的左半边和右半边,分别表示左子树和右子树的前序遍历结果。注意,这里要保证切割后的列表大小跟inorder列表切割后的大小一致,所以要去掉preorder列表第一个元素(即根节点)。

• 第六步:递归。用preorder列表和inorder列表的左半边作为参数调用buildTree方法,得到左子树的根节点,并赋值给当前根节点的左孩子。用preorder列表和inorder列表的右半边作为参数调用buildTree方法,得到右子树的根节点,并赋值给当前根节点的右孩子。

• 第七步:返回答案。返回当前根节点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值