力扣刷题#30-0112-路径总和

力扣刷题#30-0112-路径总和

题目

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于 targetSum。存在返回 true,否则返回 false

叶子节点是指没有子节点的节点。

输入: root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出: true
解释: 5 → 4 → 11 → 2 这条路径和为 22

我的思路

直觉:DFS + 回溯

看到"根到叶子的一条路径",直觉就是 DFS——一条路走到底,不对就回溯换一条。

DFS 其实就是递归的变种——递归函数自己调用自己,天然就是"先深入、后返回"的结构。树的前序/中序/后序、翻转、深度、对称,全是这个套路。


关键设计:不是累加,是递减

与其记录"走过的路径和"再和 targetSum 比,不如把 targetSum 一路减下去:

到达每个节点:targetSum -= node->val
到达叶子节点:if (targetSum == 0) → 找到路径!

好处:不用额外变量存路径和,递归参数本身就携带"还差多少"。

生活化类比

你有一张 22 元的购物卡,每经过一个商店就刷掉店里的价格。走到最后一家店(叶子)时卡刚好归零——这条路就对了。刷完还有余额或透支,就回头换条路。


我的 AC 代码

class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        if (root == nullptr)
            return false;
        if (!root->left && !root->right)
            return targetSum == root->val;

        return hasPathSum(root->left, targetSum - root->val) ||
               hasPathSum(root->right, targetSum - root->val);
    }
};

代码逐段解析

第 3-4 行:空节点

if (root == nullptr) return false;

没有节点就没有路径,返回 false。这是递归的终止条件之一。

第 5-6 行:叶子判断

if (!root->left && !root->right)
    return targetSum == root->val;

叶子 = 没有左孩子且没有右孩子。到了叶子,检查"卡里剩下的钱"是否正好等于当前商店价格。

第 8-9 行:递归下降

return hasPathSum(root->left, targetSum - root->val) ||
       hasPathSum(root->right, targetSum - root->val);

不是叶子 → 从 targetSum 里扣掉当前节点值,继续往左右走。|| 表示"左边找到路径或者右边找到路径"。

回溯的精髓:递归参数 targetSum - root->val按值传递的。递归返回后,上一层的 targetSum 保持原样——自动回到进入子树之前的状态,不需要手动恢复。


为什么 || 就是回溯?

hasPathSum(root->left, ...) 返回后,如果左边没找到(false),|| 会继续算右边的 hasPathSum(root->right, ...)——这就是"走不通换条路"。两个递归调用共享同一层剩余的 targetSum - root->val,互不干扰。


复杂度分析

维度 说明
时间复杂度 O(n) 最坏遍历所有节点
空间复杂度 O(h) 递归栈深度 = 树高 h

关键点总结

关键点 说明
核心设计 递减法:targetSum - node->val 一路传下去
叶子判断 !root->left && !root->right,中间节点不能提前判断
回溯实现 按值传递参数,递归返回自动恢复状态
DFS = 递归变种 递归调用天然"先深入后返回",树的题都是这个套路

本文档由 AI 辅助生成,作者提供问题,思路和代码,AI仅负责文本修饰,综合获得以上内容。

posted @ 2026-08-09 13:29  STA_running  阅读(4)  评论(0)    收藏  举报