Day 16 二叉树part 04

LC 513. 找树左下角的值

老规矩层序先秒了:

class Solution:
    def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
        res , queue = [] , collections.deque([root])
        while queue:
            temp = []
            for _ in range(len(queue)):
                node = queue.popleft()
                temp.append(node.val)
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
            res.append(temp)
        return res[-1][0]

递归法:

本题要找的是最底层、最左边的节点,不能一直往左子树的方向去遍历,因为最左边,可能不是最后一行!

所以正确的思路是,遍历的过程中,优先遍历左子树,当发现左子树没有的时候,再遍历右子树,此时右子树的叶子节点就是最后一层的最左边了(有点绕)。

综上,本题关键就在于求深度最大的叶子节点。

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

LC 112 路径总和

class Solution:
    def func(self , node , target):
        #递归出口
        if not node.left and not node.right and target == 0:
            return True 
        if not node.left and not node.right and target != 0:
            return False
        #单层处理逻辑
        if node.left:
            target -= node.left.val
            if self.func(node.left , target) == True: return True
            target += node.left.val
        if node.right:
            target -= node.right.val
            if self.func(node.right , target) == True: return True
            target += node.right.val

        return False
        
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int):
        if not root:
            return False
        return self.func(root , targetSum - root.val)

 再次熟悉了递归三部曲:第一,确定传入参数及返回值;第二,递归出口;第三,单层处理逻辑。本题中传入参数是根节点及target,返回的是T or F,我们的想法是逢节点便用target减去节点值,直到叶子节点,若target为0,则返回T,否则F,所以传入参数时需要手动减去根节点的值,再往下遍历;

递归出口:

当遍历到叶子节点,即为递归出口。

单层处理逻辑:

思考单层处理逻辑,可以就当作在处理根节点的下一个节点。那么首先需要判断是否为空,不为空才有单层处理逻辑。然后,进入这个节点,target减去节点值(node.val),再调用函数本身,去检测以当前节点为根节点的所有左子树,是否存在路径和为target - node.val。若存在,直接返回True,若不存在,就吧node.val加回来,继续往右子树遍历。加回来就是回溯的过程,是为了保障target不会被一直减

LC 113 路径总和Ⅱ

思路差不多,由于要遍历完,所以递归调用时,不需要返回值。

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:
            self.result.append(self.path)


        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() # 回溯

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

这么写,就错了!此处有一个极其隐蔽的坑,涉及到python当中列表是可变的特性,问题出现在这句话:

        if not cur.left and not cur.right and count == 0:
            self.result.append(self.path)

此处我们设想的是:当遇到叶子节点且满足条件时,我们将这个满足条件的路径添加到result中,但是,由于path这个列表是可变的,后面一旦修改了path,这里的结果也会跟着变,所以得不到想要的结果!

正确的做法是先copy一个备份出来,再把备份添加到结果中

        if not cur.left and not cur.right and count == 0:
            temp = self.path.copy()
            self.result.append(temp)

此处也给我们提醒了,遇到列表的时候,多思考下其可变的特性,尤其是在返回答案的时候。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值