【代码随想录day16】● 513.找树左下角的值 ● 112. 路径总和 113.路径总和ii ● 106.从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构造二叉树

513.找树左下角的值

问题

题目链接:https://leetcode.cn/problems/find-bottom-left-tree-value/
给定一个二叉树,在树的最后一行找到最左边的值。
示例:
在这里插入图片描述

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

解答

用层序遍历很简单。

# 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
from collections import deque

class Solution:
    def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
        result = []
        queue = deque([root]) # 注意deque里要输入可迭代对象
        while queue:
            layer = []
            for i in range(len(queue)):
                cur = queue.popleft()
                layer.append(cur.val)
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
            result.append(layer)
        return result[-1][0]

改进一下,不用记录整个树,只返回左下:

from collections import deque
class Solution:
    def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
        queue = deque([root])
        while queue:
            size = len(queue)
            leftmost = queue[0].val
            for i in range(size):
                node = queue.popleft()
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            if not queue:
                return leftmost

这题用递归较难
我们来分析一下题目:在树的最后一行找到最左边的值。

首先要是最后一行,然后是最左边的值。

如果使用递归法,如何判断是最后一行呢,其实就是深度最大的叶子节点一定是最后一行。

那么如何找最左边的呢?可以使用前序遍历(当然中序,后序都可以,因为本题没有 中间节点的处理逻辑,只要左优先就行),保证优先左边搜索,然后记录深度最大的叶子节点,此时就是树的最后一行最左边的值。

递归三部曲:

1.确定递归函数的参数和返回值
参数必须有要遍历的树的根节点,还有就是一个int型的变量用来记录最长深度。 这里就不需要返回值了,所以递归函数的返回类型为void。
本题还需要类里的两个全局变量,maxLen用来记录最大深度,result记录最大深度最左节点的数值。

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') #这里的值小于0就行
        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,因此对上一层递归的depth不会产生影响,因为没有改写depth的值,但传入下一层的是depth+1,实际也进入了下一层递归
            self.traversal(node.left, depth+1) 
        if node.right:
            self.traversal(node.right, depth+1)

112. 路径总和 113.路径总和ii

问题

题目链接:https://leetcode.cn/problems/path-sum/submissions/
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

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

示例:
在这里插入图片描述

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

解答

可以采用 257. 二叉树的所有路径 中的方法:

class Solution:
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if not root: return False
        result = []
        self.SumPath(root, 0, result)
        return (targetSum in result)

    def SumPath(self, root, pathsum, result):
        pathsum += root.val
        if (not root.left) and (not root.right):
            result.append(pathsum)
        if root.left:
            self.SumPath(root.left, pathsum, result)
            pathsum -= root.left.val
        if root.right:
            self.SumPath(root.right, pathsum, result)
            pathsum -= root.right.val

(为什么上述不通过)
把pathsum修改为全局变量就能出现正确的结果了:

class Solution:
    def __init__(self):
        self.pathsum = 0
    
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if not root:
            return False
        result = []
        self.SumPath(root, result)
        return targetSum in result

    def SumPath(self, root, result):
        self.pathsum += root.val
        if not root.left and not root.right:
            result.append(self.pathsum)
        if root.left:
            self.SumPath(root.left, result)
            self.pathsum -= root.left.val
        if root.right:
            self.SumPath(root.right, result)
            self.pathsum -= root.right.val

修改一下就能通过了:

class Solution:
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if not root: return False
        result = []
        self.SumPath(root, 0, result)
        return (targetSum in result)

    def SumPath(self, root, pathsum, result):
        pathsum += root.val
        if (not root.left) and (not root.right):
            result.append(pathsum)
        if root.left:
            self.SumPath(root.left, pathsum, result)
            #pathsum -= root.left.val
        if root.right:
            self.SumPath(root.right, pathsum, result)
            #pathsum -= root.right.val
        pathsum -= root.val

改成隐式回溯也能通过了

class Solution:
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if not root: return False
        result = []
        self.SumPath(root, root.val, result)
        return (targetSum in result)

    def SumPath(self, root, pathsum, result):
        if (not root.left) and (not root.right):
            result.append(pathsum)
        if root.left:
            self.SumPath(root.left, pathsum+root.left.val, result)
        if root.right:
            self.SumPath(root.right, pathsum+root.right.val, result)
class Solution:
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if not root: return False
        result = []
        self.SumPath(root, [], result)
        return (targetSum in result)

    def SumPath(self, root, pathsum, result):
        pathsum.append(root.val)
        if (not root.left) and (not root.right):
            result.append(sum(pathsum))
        if root.left:
            self.SumPath(root.left, pathsum, result)
            pathsum.pop()
        if root.right:
            self.SumPath(root.right, pathsum, result)
            pathsum.pop()

精简一下,因为上述方法会遍历整个树,但我们只需要找到一个符合条件的路径就够了:

class Solution:
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if not root: return False
        if not root.left and not root.right and targetSum == root.val:
            return True 
        return self.hasPathSum(root.left, targetSum - root.val) or self.hasPathSum(root.right, targetSum - root.val)

上述代码展开:

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) 

113.路径总和ii 是返回所有的路径和满足target,只需要在257. 二叉树的所有路径基础上修改一点就行。

import copy

class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
        if not root: return []
        result = []
        self.Path(root, [], result, targetSum)
        return result

    def Path(self, root, path, result, targetSum):
        path.append(root.val)
        if (not root.left) and (not root.right):
            if sum(path) == targetSum:
                result.append(path)
        if root.left:
            self.Path(root.left, copy.copy(path), result, targetSum)
        if root.right:
            self.Path(root.right, copy.copy(path), result, targetSum)
class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
        if not root: return []
        result = []
        self.Path(root, [], result, targetSum)
        return result

    def Path(self, root, path, result, targetSum):
        path.append(root.val)
        if (not root.left) and (not root.right):
            a = [i for i in path]
            if sum(a) == targetSum:
                result.append(a)
        if root.left:
            self.Path(root.left, path, result, targetSum)
            path.pop()
        if root.right:
            self.Path(root.right, path, result, targetSum)
            path.pop()
class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
        if not root: return []
        result = []
        self.Path(root, [], result, targetSum)
        return result

    def Path(self, root, path, result, targetSum):
        path.append(root.val)
        if (not root.left) and (not root.right):
            if sum(path) == targetSum:
                result.append(list(path))
        if root.left:
            self.Path(root.left, path, result, targetSum)
            path.pop()
        if root.right:
            self.Path(root.right, path, result, targetSum)
            path.pop()

该题目还有很多问题,之后汇总总结一下(似乎是全局变量和中间变量的问题,但还是有没法解释的结果)

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

问题

题目链接:https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/
给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

示例:
在这里插入图片描述

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

提示:树的每个元素一定不同。

解答

顺序和我们自己创建树的过程类似:
第一步:如果数组大小为零的话,说明是空节点了。

第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。

第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点

第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)

第五步:切割后序数组,切成后序左数组和后序右数组

第六步:递归处理左区间和右区间

难点在于切割数组时采用的左右区间的开闭。我们在递归的时候要保持这个不改变。

A.首先要切割中序数组,为什么先切割中序数组呢?

切割点在后序数组的最后一个元素,就是用这个元素来切割中序数组的,所以必要先切割中序数组。

中序数组相对比较好切,找到切割点(后序数组的最后一个元素)在中序数组的位置,然后切割

B.接下来就要切割后序数组了

首先后序数组的最后一个元素指定不能要了,这是切割点 也是 当前二叉树中间节点的元素,已经用了。

后序数组的切割点怎么找?

后序数组没有明确的切割元素来进行左右切割,不像中序数组有明确的切割点,切割点左右分开就可以了。

此时有一个很重的点,就是中序数组大小一定是和后序数组的大小相同的(这是必然)。

中序数组我们都切成了左中序数组和右中序数组了,那么后序数组就可以按照左中序数组的大小来切割,切成左后序数组和右后序数组。

此时,中序数组切成了左中序数组和右中序数组,后序数组切割成左后序数组和右后序数组。

接下来可以递归了。

class Solution:
    def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
        if not inorder: return None

        #在后序找到根节点
        root_val = postorder[-1]
        root = TreeNode(root_val)

        #在中序找到根节点索引
        root_index = inorder.index(root_val) #index()函数将返回第一个匹配到的元素的索引位置

        #在中序切分左右子树
        left_subtree_in = inorder[:root_index]
        right_subtree_in = inorder[root_index+1:]

        #在后序切分左右子树
        #左右子树的长度在中序和后序一定相同
        left_subtree_post = postorder[:len(left_subtree_in)]
        right_subtree_post = postorder[len(left_subtree_in):-1] #这里的分割区间一定要谨慎
        #right_subtree_post = postorder[len(left_subtree_in):len(left_subtree_in)+len(right_subtree_in)]也可以

        #递归
        root.left = self.buildTree(left_subtree_in, left_subtree_post)
        root.right = self.buildTree(right_subtree_in, right_subtree_post)

        return root

前序和后序类似,区别是一个从前往后取,一个从后往前取:

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
        if not inorder: return None

        #在前序找到根节点
        root_val = preorder[0]
        root = TreeNode(root_val)

        #在中序找到根节点索引
        root_index = inorder.index(root_val) #index()函数将返回第一个匹配到的元素的索引位置

        #在中序切分左右子树
        left_subtree_in = inorder[:root_index]
        right_subtree_in = inorder[root_index+1:]

        #在前序切分左右子树
        #左右子树的长度在中序和前序一定相同
        left_subtree_pre = preorder[1:len(left_subtree_in)+1]
        right_subtree_pre = preorder[len(left_subtree_in)+1:] #这里的分割区间一定要谨慎

        #递归
        root.left = self.buildTree(left_subtree_pre, left_subtree_in)
        root.right = self.buildTree(right_subtree_pre, right_subtree_in)

        return root
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值