☰
LeetCode 112题解析:二叉树路径总和算法与优化
2026/10/28 4:54:54 网站建设 项目流程

1. 问题背景与题目解析

LeetCode 112题"路径总和"是二叉树类问题中的经典题目,也是面试中的高频考点。题目要求判断二叉树中是否存在从根节点到叶子节点的路径,使得路径上所有节点值之和等于给定的目标值。

这道题之所以重要,是因为它考察了两个核心能力:

  • 对二叉树结构的理解与操作能力
  • 递归思想的实际应用能力

在实际开发中,类似路径搜索的场景非常常见,比如:

  • 文件系统中查找特定大小的文件
  • 组织架构树中查找满足条件的汇报路径
  • 游戏开发中的路径决策算法

2. 基础解法:递归DFS

2.1 递归思路解析

递归解法是最直观的解决方案,其核心思想是:

  1. 从根节点开始,每次递归处理当前节点
  2. 将目标值减去当前节点的值
  3. 如果当前节点是叶子节点,判断剩余值是否为0
  4. 如果不是叶子节点,则递归处理左右子树
def hasPathSum(root, targetSum): if not root: return False if not root.left and not root.right: return targetSum == root.val return hasPathSum(root.left, targetSum - root.val) or \ hasPathSum(root.right, targetSum - root.val)

2.2 递归实现细节

  • 时间复杂度:O(n),需要访问每个节点一次
  • 空间复杂度:O(h),h为树的高度,递归调用栈的深度

注意:递归解法虽然简洁,但在处理大型树时可能会遇到栈溢出问题。对于极端不平衡的树(如链表状的树),空间复杂度会退化为O(n)。

2.3 递归优化技巧

  1. 提前终止:当找到符合条件的路径时立即返回,不再继续搜索
  2. 短路求值:使用or运算符,左子树找到结果就不搜索右子树
  3. 尾递归优化:某些语言支持尾递归优化,可以避免栈溢出

3. 进阶解法:迭代DFS

3.1 迭代思路解析

迭代解法使用显式的栈来模拟递归过程:

  1. 使用栈存储节点和对应的剩余目标值
  2. 每次从栈中弹出一个节点和剩余值
  3. 如果是叶子节点且剩余值等于节点值,返回True
  4. 否则将左右子节点和新的剩余值压入栈中
def hasPathSum(root, targetSum): if not root: return False stack = [(root, targetSum)] while stack: node, curr_sum = stack.pop() if not node.left and not node.right and curr_sum == node.val: return True if node.right: stack.append((node.right, curr_sum - node.val)) if node.left: stack.append((node.left, curr_sum - node.val)) return False

3.2 迭代实现细节

  • 时间复杂度:O(n),同样需要访问每个节点
  • 空间复杂度:O(h),栈的最大深度为树的高度

提示:迭代解法中节点的处理顺序会影响搜索顺序。上述代码使用后进先出的栈,实现的是深度优先搜索。如果使用队列(先进先出),则变为广度优先搜索。

3.3 迭代与递归的选择

选择依据:

  • 树的高度较大时,优先选择迭代解法避免栈溢出
  • 代码简洁性要求高时,选择递归解法
  • 需要控制搜索顺序时,迭代解法更灵活

4. 边界条件与异常处理

4.1 常见边界情况

  1. 空树:直接返回False
  2. 单节点树:判断节点值是否等于目标值
  3. 负数值节点:不影响算法正确性
  4. 目标值为0:只有当树为空时才返回True

4.2 测试用例设计

完整的测试应包含:

# 空树 assert hasPathSum(None, 0) == False # 单节点树 root = TreeNode(5) assert hasPathSum(root, 5) == True assert hasPathSum(root, 6) == False # 多节点树 root = TreeNode(5) root.left = TreeNode(4) root.right = TreeNode(8) root.left.left = TreeNode(11) root.left.left.left = TreeNode(7) root.left.left.right = TreeNode(2) root.right.left = TreeNode(13) root.right.right = TreeNode(4) root.right.right.right = TreeNode(1) assert hasPathSum(root, 22) == True assert hasPathSum(root, 26) == True assert hasPathSum(root, 18) == False

5. 算法扩展与变种

5.1 返回所有满足条件的路径

LeetCode 113题是本题的扩展,要求返回所有满足条件的路径。解法思路类似,但需要维护当前路径:

def pathSum(root, targetSum): def dfs(node, curr_sum, path, res): if not node: return if not node.left and not node.right and curr_sum == node.val: res.append(path + [node.val]) return dfs(node.left, curr_sum - node.val, path + [node.val], res) dfs(node.right, curr_sum - node.val, path + [node.val], res) res = [] dfs(root, targetSum, [], res) return res

5.2 任意节点开始的路径

LeetCode 437题进一步扩展,路径不需要从根节点开始。这需要双重递归:

def pathSum(root, targetSum): if not root: return 0 return (dfs(root, targetSum) + pathSum(root.left, targetSum) + pathSum(root.right, targetSum)) def dfs(node, curr_sum): if not node: return 0 return (int(curr_sum == node.val) + dfs(node.left, curr_sum - node.val) + dfs(node.right, curr_sum - node.val))

5.3 实际应用场景

  1. 文件系统搜索:查找特定大小的文件路径
  2. 组织架构分析:查找满足条件的汇报路径
  3. 游戏AI决策:评估不同路径的得分

6. 性能优化与进阶思考

6.1 记忆化优化

对于重复计算的子树,可以使用记忆化技术存储中间结果:

from functools import lru_cache @lru_cache(maxsize=None) def hasPathSum(root, targetSum): # 实现略

6.2 并行计算

对于大型树,可以考虑并行处理左右子树:

from concurrent.futures import ThreadPoolExecutor def hasPathSum(root, targetSum): if not root: return False if not root.left and not root.right: return targetSum == root.val with ThreadPoolExecutor() as executor: left = executor.submit(hasPathSum, root.left, targetSum - root.val) right = executor.submit(hasPathSum, root.right, targetSum - root.val) return left.result() or right.result()

6.3 算法选择建议

  1. 小型树:优先选择递归解法,代码简洁
  2. 大型树:选择迭代解法,避免栈溢出
  3. 需要所有路径:使用回溯法
  4. 并行环境:考虑并行计算优化

7. 常见错误与调试技巧

7.1 典型错误

  1. 忘记处理空树情况
  2. 错误判断叶子节点(使用or而不是and)
  3. 递归终止条件错误
  4. 迭代解法中节点处理顺序错误

7.2 调试方法

  1. 打印递归调用树
def hasPathSum(root, targetSum, depth=0): print(" "*depth + f"Node: {root.val if root else None}, Target: {targetSum}") # 其余代码不变
  1. 可视化二叉树结构
def printTree(root, level=0): if root: printTree(root.right, level + 1) print(" "*level + str(root.val)) printTree(root.left, level + 1)
  1. 使用小规模测试用例逐步验证

8. 相关题目推荐

  1. 简单难度:

      1. 二叉树的最大深度
      1. 平衡二叉树
  2. 中等难度:

      1. 路径总和 II
      1. 求根到叶子节点数字之和
  3. 困难难度:

      1. 二叉树中的最大路径和
      1. 路径总和 III

9. 学习资源与进阶路径

  1. 推荐书籍:

    • 《算法导论》树结构章节
    • 《编程珠玑》算法优化部分
  2. 在线课程:

    • LeetCode二叉树专题
    • Coursera算法专项课程
  3. 练习建议:

    • 先掌握基础遍历方法
    • 再学习递归思想
    • 最后研究优化技巧

10. 面试技巧与实战建议

  1. 面试常见问题:

    • 解释算法时间/空间复杂度
    • 如何处理边界条件
    • 递归和迭代的区别
  2. 回答策略:

    • 先说明解题思路
    • 再写代码
    • 最后分析复杂度
  3. 实战建议:

    • 先写测试用例
    • 考虑异常情况
    • 讨论优化空间

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询