题目描述

✅ 112. 路径总和

image-20260928195108484

image-20260928195108485

题意分析

判断是否存在一条从根节点出发、沿父子连接一直到叶子节点的路径,使路径中所有节点值之和等于 targetSum。只需返回是否存在,不需要记录路径;任何一条满足即可。

叶子节点必须左右孩子都为空,中途节点或某个缺失的孩子都不能充当终点。空树不存在根到叶子的路径,即使目标为零也返回 false。节点值可能为负数,因此不能只依据当前和是否超过目标判断后面有没有答案。

解法:DFS 递减剩余路径和

核心思路

[!blue]

给递归函数一个明确含义:hasPathSum(node, remain) 判断,从当前 node 开始到它所在子树的某片叶子,是否存在节点和等于 remain 的路径。原问题就是从根节点出发、剩余目标为 targetSum 的这次调用。

若当前节点为空,这里没有可作为路径起点的节点,应直接返回 false。若当前节点是叶子,整条剩余路径只有它自己,只需比较 remain == node.val;不能把叶子判断替换成空节点处比较剩余值,否则只有一个孩子的中间节点可能沿空分支被误认为路径终点。

若当前节点不是叶子,任何完整路径都必须先包含它,再进入左子树或右子树。因此扣除当前值,把 remain - node.val 分别交给两个孩子,只要其中一个子问题成立,当前问题就成立。这既覆盖所有根到叶子的选择,也不会把不同分支的节点和混合起来。

左右子调用接收的是同一个扣除结果,但整数参数按值传递,左分支的递归不会改变右分支需要的目标,不需要手动恢复数值。逻辑或会在左侧已找到答案时直接结束,符合本题只判断存在性的要求;如果两侧都不成立才返回 false。

解题步骤

  1. 当前节点为空时返回 false。
  2. 当前节点左右孩子都为空时,返回目标是否等于当前节点值。
  3. 否则计算 remain = targetSum - root.val,把当前节点的贡献扣掉一次。
  4. 用同一个 remain 递归判断左右子树,返回两次结果的逻辑或。

代码实现

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return false;
        }

        // 目标仍包含当前节点,只有完整到达叶子才判断相等。
        if (root.left == null && root.right == null) {
            return targetSum == root.val;
        }

        // 扣掉当前节点后,把剩余目标交给孩子。
        int remain = targetSum - root.val;

        return hasPathSum(root.left, remain) || hasPathSum(root.right, remain);
    }
}
func hasPathSum(root *TreeNode, targetSum int) bool {
    if root == nil {
        return false
    }
    // 目标仍包含当前节点,只有完整到达叶子才判断相等。
    if root.Left == nil && root.Right == nil {
        return targetSum == root.Val
    }

    // 扣掉当前节点后,把剩余目标交给孩子。
    remain := targetSum - root.Val
    return hasPathSum(root.Left, remain) || hasPathSum(root.Right, remain)
}

复杂度分析

  • 时间复杂度:$O(n)$,最坏访问所有节点;找到匹配路径后可以提前结束。
  • 空间复杂度:$O(h)$,h 为树高,递归栈只保存当前路径。平衡树为 $O(\log n)$,退化为链时为 $O(n)$。

关键点总结

[!green]

  • 递归目标仍包含当前节点,因此叶子直接比较当前值,非叶子才把扣除后的目标传给孩子。
  • 只有真实叶子能够结束路径,空节点表示没有路径而不是成功匹配。
  • 子树之间是“任意一条成立即可”的关系,返回值使用逻辑或。
  • 目标按值传递,不会发生左右分支互相污染,不需要另建路径列表。

易错点总结

[!yellow]

  • 在空节点处用剩余目标为零判断成功,会把中间节点的缺失孩子当作终点;空节点必须返回 false。
  • 只要当前值等于剩余目标就返回成功,可能在尚未到达叶子时提前结束。
  • 用左右孩子为空的“或”条件判断叶子,会把只有一个孩子的节点误判为叶子。
  • 先扣除当前值,又在叶子处与当前值比较,相当于重复扣除;必须统一剩余目标的含义。
  • 按剩余目标的正负剪枝,会忽略后续负数节点可能补足路径和的情况。

相似题目

题目 难度 关联与区别
113. 路径总和 II 中等 路径范围同为根到叶,原题输出全部匹配路径,本题只需判断是否存在。
437. 路径总和 III 中等 原题允许任意祖先作为起点,本题起点固定为根且终点必须是叶子。
257. 二叉树的所有路径 简单 回溯维护从根到当前节点的路径;本题判断是否存在目标和叶路径,该题输出所有根到叶的字符串路径。
129. 求根节点到叶节点数字之和 中等 回溯维护从根到当前节点的路径;本题判断是否存在目标和叶路径,该题逐层按十进制累加根到叶数字。
666. 路径总和 IV 中等 路径总和系列。IV 用深度和位置编码树,仍沿根到叶累计路径和,最终对所有叶子的结果求和。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/60277569
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!