代码随想录算法-day16

前言


513.找树左下角的值

重点体会size的标记作用。


113. 路径总和II

本节只讲递归法,实际上也可采用迭代法求解问题。“112.路径总和”也可以用类似方法求解。
首先是有错误的题解(连测试用例1都没有通过),仔细看到底错在哪里了:

# 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 root==None: return []
        self.curPath, self.result = [root.val], []
        self.count = targetSum - root.val
        self.traverse(root)
        return self.result
        
    def traverse(self, node):
        if not node.left and not node.right and self.count==0:
            self.result.append(self.curPath)
        if node.left:
            self.curPath.append(node.left.val)
            self.count -= node.left.val
            self.traverse(node.left)
            self.curPath.pop()
            self.count += node.left.val
        if node.right:
            self.curPath.append(node.right.val)
            self.count -= node.right.val
            self.traverse(node.right)
            self.curPath.pop()
            self.count += node.right.val
        return        

是不是没有头绪?其实俺也一样,一开始想破了脑袋都没找到bug,于是灵机一动使用Deepseek帮我分析,DS用了34秒就把问题解决了,还顺带分析了代码时间、空间复杂度都是O(n),并且直接指出我的代码可以通过LeetCode的测试(这水平令人心塞哇)。

路径引用问题​​ (核心错误)
在self.result.append(self.curPath)中直接添加curPath的引用,而非拷贝。由于Python列表是可变对象,后续的pop()操作会修改已添加到结果中的路径,导致最终结果全部为空或错误。
​​修正方法​​:使用self.curPath.copy()或list(self.curPath)创建副本

DS给出的题解如下,它还贴心地加上了注释(doge):

class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
        if not root:
            return []
        self.result = []
        self.curPath = [root.val]  # 初始化路径包含根节点值
        self.count = targetSum - root.val  # 计算初始差值
        self.traverse(root)
        return self.result
        
    def traverse(self, node):
        # 到达叶子节点且路径和等于目标值时,保存路径副本
        if not node.left and not node.right and self.count == 0:
            self.result.append(self.curPath.copy())  # 关键修正:深拷贝路径
        
        # 递归处理左子树
        if node.left:
            self.curPath.append(node.left.val)
            self.count -= node.left.val
            self.traverse(node.left)
            self.curPath.pop()  # 回溯
            self.count += node.left.val
        
        # 递归处理右子树
        if node.right:
            self.curPath.append(node.right.val)
            self.count -= node.right.val
            self.traverse(node.right)
            self.curPath.pop()  # 回溯
            self.count += node.right.val

建议大家以后也可以尝试用DS改bug(可以延伸到所有需要及时反馈的场合),像和老师交谈一样,效率快很多。

这里再浅浅谈下递归的感受

所谓递归,就是利用函数的嵌套(通常是深层嵌套),使得从数据结构的底层自下往上开始遍历,所以递归有终止条件Base Case、递归调用Recusive Call和当前层处理逻辑Current Step Logic三要素。

还有就是回溯
看看下面这段代码,“撤销”一词非常形象!从遍历的角度上看,只有到达底层叶子节点且路径长度符合要求时递归函数才会返回结果到self.result,除此之外的搜索后会自动撤销进行向上回溯。

        # 递归处理左子树
        if node.left:
            self.curPath.append(node.left.val)
            self.count -= node.left.val
            self.traverse(node.left)
            self.curPath.pop()  # 回溯关键步骤,当层递归返回后撤销本层操作
            self.count += node.left.val 

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

逻辑上还是比较复杂,先记录下第一次AC的题解,二刷的时候肯定要尝试更加精简。

# 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 inorder == []: return []        
        self.biTree = self.travseBetween(inorder, postorder)
        return self.biTree

    def travseBetween(self, cur_inorder, cur_postorder):
        if cur_inorder == []: return 
        midNode_val = cur_postorder[-1]
        midNode = TreeNode(midNode_val)
        i = 0
        while i  < len(cur_inorder):
            if midNode_val != cur_inorder[i]:
                i += 1
            else:
                left_inorder = cur_inorder[:i]
                left_postorder = cur_postorder[:i]
                right_inorder = cur_inorder[i+1:]
                right_postorder = cur_postorder[i:-1]
                midNode.left = self.travseBetween(left_inorder, left_postorder)
                midNode.right = self.travseBetween(right_inorder, right_postorder)
                break
        return midNode

谈点题外话
做这道题的时候,一开始脑子想的是:“如果可以更加中序和后序数组求得构造出二叉树,肯定可以使用数学公式得到某种表达式”。现在认识到,不少算法设计依赖​​过程性逻辑、启发式规则或复杂状态转移​​,需通过步骤描述(需要代码实现)而非单一公式,比如本题


### 关于代码随想录 Day04 的学习资料与解析 #### 一、Day04 主要内容概述 代码随想录 Day04 的主要内容围绕 **二叉树的遍历** 展开,包括前序、中序和后序三种遍历方式。这些遍历可以通过递归实现,也可以通过栈的方式进行迭代实现[^1]。 #### 二、二叉树的遍历方法详解 ##### 1. 前序遍历(Pre-order Traversal) 前序遍历遵循访问顺序:根节点 -> 左子树 -> 右子树。以下是基于递归的实现: ```python def preorderTraversal(root): result = [] def traversal(node): if not node: return result.append(node.val) # 访问根节点 traversal(node.left) # 遍历左子树 traversal(node.right) # 遍历右子树 traversal(root) return result ``` 对于迭代版本,则可以利用显式的栈来模拟递归过程: ```python def preorderTraversal_iterative(root): stack, result = [], [] current = root while stack or current: while current: result.append(current.val) # 访问当前节点 stack.append(current) # 将当前节点压入栈 current = current.left # 转向左子树 current = stack.pop() # 弹出栈顶元素 current = current.right # 转向右子树 return result ``` ##### 2. 中序遍历(In-order Traversal) 中序遍历遵循访问顺序:左子树 -> 根节点 -> 右子树。递归实现如下: ```python def inorderTraversal(root): result = [] def traversal(node): if not node: return traversal(node.left) # 遍历左子树 result.append(node.val) # 访问根节点 traversal(node.right) # 遍历右子树 traversal(root) return result ``` 迭代版本同样依赖栈结构: ```python def inorderTraversal_iterative(root): stack, result = [], [] current = root while stack or current: while current: stack.append(current) # 当前节点压入栈 current = current.left # 转向左子树 current = stack.pop() # 弹出栈顶元素 result.append(current.val) # 访问当前节点 current = current.right # 转向右子树 return result ``` ##### 3. 后序遍历(Post-order Traversal) 后序遍历遵循访问顺序:左子树 -> 右子树 -> 根节点。递归实现较为直观: ```python def postorderTraversal(root): result = [] def traversal(node): if not node: return traversal(node.left) # 遍历左子树 traversal(node.right) # 遍历右子树 result.append(node.val) # 访问根节点 traversal(root) return result ``` 而迭代版本则稍复杂一些,通常采用双栈法或标记法完成: ```python def postorderTraversal_iterative(root): if not root: return [] stack, result = [root], [] while stack: current = stack.pop() result.insert(0, current.val) # 插入到结果列表头部 if current.left: stack.append(current.left) # 先压左子树 if current.right: stack.append(current.right) # 再压右子树 return result ``` #### 三、补充知识点 除了上述基本的二叉树遍历外,Day04 还可能涉及其他相关内容,例如卡特兰数的应用场景以及组合问题的基础模板[^2][^4]。如果遇到具体题目,可以根据实际需求调用相应算法工具。 --- ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值