Leetcode: 路径总和 III

28 阅读2分钟

原题:leetcode.cn/problems/pa…

题目描述

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。

路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

解题思路

  1. 双重DFS
    • 外层DFS遍历所有节点作为路径起点
    • 内层DFS计算以当前节点为起点的路径和
  2. 前缀和优化
    • 哈希表记录路径前缀和出现次数
    • 当前前缀和 - targetSum 查表获得有效路径数
  3. 回溯维护:递归时更新哈希表,回溯时恢复状态
  4. 边界处理:初始化哈希表 {0:1} 处理根节点自身满足条件的情况

关键洞察

  1. 子问题分解:任意节点都可作为路径起点或中间点
  2. 前缀和原理当前和 - targetSum = 历史前缀和 即存在有效路径
  3. 哈希表加速:将O(n²)暴力搜索优化为O(n)查询
  4. 回溯必要性:确保哈希表仅记录当前路径的前缀和
  5. 空路径初始化{0:1}处理路径正好等于targetSum的情况

复杂度分析

  • 时间复杂度
    • 暴力DFS:O(n²)
    • 前缀和+DFS:O(n)
  • 空间复杂度O(n)
    • 递归栈O(h) + 哈希表O(n)(h为树高)

代码实现

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} targetSum
 * @return {number}
 */
var pathSum = function (root, targetSum) {
    if (!root) return 0;
    // 初始化前缀和映射表(前缀和 -> 出现次数)
    const prefixSum = new Map();
    prefixSum.set(0, 1);  // 关键:空路径前缀和为0
    let count = 0;
    const dfs = (node, currentSum) => {
        if (!node) return;
        // 计算当前节点前缀和
        currentSum += node.val;
        // 检查是否存在有效路径:当前和 - targetSum = 历史前缀和
        const prevSum = currentSum - targetSum;
        count += prefixSum.get(prevSum) || 0;
        // 更新当前前缀和的计数
        prefixSum.set(currentSum, (prefixSum.get(currentSum) || 0) + 1);
        // 递归遍历子树
        dfs(node.left, currentSum);
        dfs(node.right, currentSum);
        // 回溯:移除当前节点贡献的前缀和
        prefixSum.set(currentSum, prefixSum.get(currentSum) - 1);
    };
    dfs(root, 0);
    return count;
};

相似题目