代码随想录算法训练营第十八天 | 二叉树 part 5 | 513. 找树左下角的值、路径总和、从中序与后序遍历序列构造二叉树

目录

  • 513. 找树左下角的值
    • 思路
    • 代码
    • 要点
  • 112. 路径总和
    • 思路
  • 113. 路径总和 ii
    • 思路
    • 代码
    • 要点
  • 106.从中序与后序遍历序列构造二叉树
    • 思路
    • 代码

513. 找树左下角的值

Leetcode

代码随想录算法训练营第十八天 | 二叉树 part 5 | 513. 找树左下角的值、路径总和、从中序与后序遍历序列构造二叉树_第1张图片

思路

最简单的一种是用层序遍历。BFS

递归的话也是可以的,只是稍微有些复杂。使用两个全局变量maxDepthres,记录最大深度下的叶子节点的值。

因为我们遍历的时候先遍历左枝,所以在最大深度记录的第一个叶子节点就是左下角的值。

代码

递归

# 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:
        maxDepth = float("-inf")
        res = 0

        def dfs(root, depth):
            nonlocal maxDepth
            nonlocal res

            if not root.left and not root.right:
                if depth > maxDepth:
                    maxDepth = depth
                    res = root.val
                return

            if root.left:
                dfs(root.left, depth + 1)
            if root.right:
                dfs(root.right, depth + 1)
        
        dfs(root, 0)
        return res

层序遍历

# 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:
            level = []
            for i in range(len(q)):
                node = q.popleft()
                level.append(node.val)
                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)

        return level[0]

要点

  • 在递归的时候,上级变量在局部可以注明nonlocal。Python中的变量有两个部分:变量的名称和它的值。对于可变对象,我们可以直接修改它的值,而不需要改变变量的引用。对于不可变对象,我们需要使用nonlocal关键字来修改它的值。这就是为什么在嵌套函数中,我们可以直接修改列表或字典,但不能直接修改数字或字符串的原因。
    代码随想录算法训练营第十八天 | 二叉树 part 5 | 513. 找树左下角的值、路径总和、从中序与后序遍历序列构造二叉树_第2张图片
  • 同时也可以在初始化的时候用self.maxDepth, self.res的方式,这样子变量会变成全局变量。

112. 路径总和

Leetcode

代码随想录算法训练营第十八天 | 二叉树 part 5 | 513. 找树左下角的值、路径总和、从中序与后序遍历序列构造二叉树_第3张图片

思路

具体的思路是前序遍历,并记录下遍历到叶节点的总和,如果等于targetSum则返回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 not root:
            return False

        def helper(node, total, target):
            if not node.left and not node.right:
                temp = total+node.val
                if temp == target:
                    return True

            if node.left:
                helper(node.left, total + node.val, target)

            if node.right:
                helper(node.right, total + node.val, target)

        return True if helper(root, 0, targetSum) else False

我跑的时候发现,结果只会return False,不会return True。但是我在print(temp)之后发现遍历的值没有什么问题。而且我尝试打印出来helper(root, 0, targetSum),结果是None

于是我加入了一个全局变量res来储存这个True结果。

# 第二份,加入nonlocal res
# 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 not root:
            return False
        
        res = False

        def helper(node, total, target):
            nonlocal res
            if not node.left and not node.right:
                temp = total+node.val
                if temp == target:
                    res = True
            if node.left:
                helper(node.left, total + node.val, target)

            if node.right:
                helper(node.right, total + node.val, target)

        helper(root, 0, targetSum)
        return res

这下跑通了,但是为什么第一份代码不行呢?

我在问了chatGPT之后发现,跑不通的原因是,我递归得到的这份True,并不能传回到上一级,上一级没有return任何东西,所以传回到上一级之后,还是return None。所以我做出了如下修改,这下能跑了:

# 第三份,为第一份的修改版
# 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 not root:
            return False

        def helper(node, total, target):
            if not node.left and not node.right:
                temp = total + node.val
                if temp == target:
                    return True

            left_res = helper(node.left, total + node.val, target) if node.left else False
            right_res = helper(node.right, total + node.val, target) if node.right else False

            return left_res or right_res

        return helper(root, 0, targetSum)

这一份代码在最后会使用or来串起递归的每一份return,从而保证只要有一个True,就能被主函数返回。

113. 路径总和 ii

Leetcode

代码随想录算法训练营第十八天 | 二叉树 part 5 | 513. 找树左下角的值、路径总和、从中序与后序遍历序列构造二叉树_第4张图片

思路

思路和前一题类似,但是路径总和ii要遍历整个树,找到所有路径,所以递归函数不需要返回值。

代码随想录算法训练营第十八天 | 二叉树 part 5 | 513. 找树左下角的值、路径总和、从中序与后序遍历序列构造二叉树_第5张图片

代码

# 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]]:
        if not root:
            return []
        res = []
        def helper(node, total, path, target):
            path.append(node.val)
            if not node.left and not node.right:
                if total + node.val == target:
                	# 此处一定要是path.copy(), 否则传递进res的是一份引用
                    res.append(path.copy())

            if node.left:
                helper(node.left, total + node.val, path, target)
                path.pop()
            if node.right:
                helper(node.right, total + node.val, path, target)
                path.pop()

        helper(root, 0, [], targetSum)
        return res

要点

  • res.append(path.copy())这里记得需要使用path.copy(),否则之后的path.pop()也会影响到append到res里面的path
  • 当然这里也可以把回溯隐藏在path里面,传递变量的时候传递path[:]即可。

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

Leetcode

代码随想录算法训练营第十八天 | 二叉树 part 5 | 513. 找树左下角的值、路径总和、从中序与后序遍历序列构造二叉树_第6张图片

思路

后序遍历会告诉我们根节点的位置,根据根节点的位置回到中序遍历可以找到左枝和右枝的位置。
代码随想录算法训练营第十八天 | 二叉树 part 5 | 513. 找树左下角的值、路径总和、从中序与后序遍历序列构造二叉树_第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
class Solution:
    def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
        if not inorder:
            return None
        
        target = postorder.pop()
        ind = inorder.index(target)
        root = TreeNode(val = target) # 中
        # 先root.right,因为后序遍历的顺序是左右中,倒过来就是中右左
        root.right = self.buildTree(inorder[ind + 1:], postorder) #左
        root.left = self.buildTree(inorder[:ind], postorder)# 右
        return root

前序遍历和中序遍历

# 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]:
    	# 判定条件是inorder而不是preorder,因为inorder会于preorder先一步变空。
        if not inorder:
            return None
        
        # 注意pop的位置,首位
        ind = inorder.index(preorder.pop(0))
        root = TreeNode(val = inorder[ind])
        root.left = self.buildTree(preorder, inorder[:ind])
        root.right = self.buildTree(preorder, inorder[ind + 1:])
        return root

可以改进的方法,查询index的操作每次耗时O(n),可以用哈希表提前记录好index和value。同时我们注意到preorder或者postorder没必要放进循环变量里面,只需要放入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]:
        idxDict = {v: i for i, v in enumerate(inorder)}

        def helper(l, r):
            if l > r:
                return None
            
            target = preorder.pop(0)
            ind = idxDict[target]
            root = TreeNode(val = inorder[ind])
            root.left = helper(l, ind - 1)
            root.right = helper(ind + 1, r)
            return root

        return helper(0, len(inorder) - 1)

这样子改完之后,时间从O(n^2)降低到了O(n)

你可能感兴趣的:(代码随想录算法训练营,算法,数据结构,leetcode,python)