LeetCode算法题(Go语言实现)_36

题目

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

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

一、代码实现(双重递归法)

go 复制代码
func pathSum(root *TreeNode, targetSum int) int {
    if root == nil {
        return 0
    }
    // 计算以当前节点为起点的路径数 + 左右子树的路径数
    return dfs(root, targetSum) + 
           pathSum(root.Left, targetSum) + 
           pathSum(root.Right, targetSum)
}

func dfs(node *TreeNode, remain int) int {
    if node == nil {
        return 0
    }
    count := 0
    if node.Val == remain {
        count++
    }
    // 递归处理左右子树,更新剩余目标值
    count += dfs(node.Left, remain - node.Val)
    count += dfs(node.Right, remain - node.Val)
    return count
}

二、算法分析(递归法)

1. 核心思路
  • 双重递归结构:外层递归遍历所有节点作为路径起点,内层递归计算以该节点为起点的路径数目
  • 暴力穷举:对每个节点及其子树进行深度优先搜索,统计符合条件的路径
2. 关键步骤
  1. 外层递归:遍历每个节点作为可能的路径起点
  2. 内层递归 :以当前节点为起点,向下搜索满足sum=targetSum的路径
  3. 终止条件:空节点返回0,当前节点值等于剩余值时计数+1
  4. 状态传递 :将剩余值remain - node.Val传递给子树
3. 复杂度
指标 说明
时间复杂度 O(n²) 每个节点触发一次O(n)的子树遍历
空间复杂度 O(h) h为树高度(递归栈空间)

三、代码实现(前缀和优化法)

go 复制代码
func pathSum(root *TreeNode, targetSum int) int {
    prefixSum := make(map[int]int)
    prefixSum[0] = 1 // 处理根节点自身满足条件的情况
    return dfs(root, 0, targetSum, prefixSum)
}

func dfs(node *TreeNode, currSum int, target int, prefixSum map[int]int) int {
    if node == nil {
        return 0
    }
    
    currSum += node.Val
    count := prefixSum[currSum - target]
    
    prefixSum[currSum]++
    count += dfs(node.Left, currSum, target, prefixSum)
    count += dfs(node.Right, currSum, target, prefixSum)
    prefixSum[currSum]-- // 回溯
    
    return count
}

四、算法分析(前缀和法)

1. 核心思路
  • 前缀和哈希表:记录从根节点到当前节点的路径和出现次数
  • 数学关系 :若存在currSum - target = oldSum,则oldSum -> currSum的路径和为target
2. 关键步骤
  1. 初始化哈希表 :预存prefixSum[0]=1处理根节点自身满足条件的情况
  2. 更新当前和 :累加节点值到currSum
  3. 查询差值 :计算currSum - target的出现次数
  4. 回溯操作:维护哈希表状态避免子树间的干扰
3. 复杂度
指标 说明
时间复杂度 O(n) 单次遍历所有节点
空间复杂度 O(n) 哈希表存储n个节点的前缀和

五、图解示例

root = [10,5,-3,3,2,null,11], targetSum=8为例:

复制代码
        10
       /  \
      5   -3
     / \    \
    3   2   11

前缀和法流程

  1. 路径10→5→3:和为18 → 18-8=10(无记录)
  2. 路径10→5→2:和为17 → 17-8=9(无记录)
  3. 路径10→5:和为15 → 15-8=7(无记录)
  4. 路径10→-3→11:和为18 → 18-8=10(命中初始0)
    最终计数:3(路径5→3、路径5→2→1、路径-3→11)

六、边界条件与扩展

1. 特殊场景验证
  • 空树:返回0
  • 负数和零 :如root = [-2,null,-3], target=-5 → 返回1
  • 重复路径:多节点值相同的情况需正确计数
2. 扩展应用
  • 多条件路径统计:同时满足和值与节点数限制
  • 动态目标值:支持实时修改targetSum的在线查询
  • 路径回溯:记录具体路径而非仅计数(需维护路径栈)

七、总结与优化方向

1. 方法对比
方法 优势 劣势 适用场景
双重递归 实现简单 时间复杂度高 小规模树(n<1000)
前缀和 线性时间复杂度 需要维护哈希表状态 大规模树
2. 工程优化
  • 并行计算:对左右子树进行并发遍历(Go协程)
  • 内存预分配:根据树高度预判哈希表容量
  • 数值溢出处理:使用int64存储累加和
3. 算法扩展
  • K路径问题:统计和值为targetSum的TopK最长路径
  • 三维路径:推广到三叉树等复杂结构
  • 流式处理:支持无法完整加载内存的超大树结构
相关推荐
草履虫建模17 小时前
力扣算法 1768. 交替合并字符串
java·开发语言·算法·leetcode·职场和发展·idea·基础
naruto_lnq19 小时前
分布式系统安全通信
开发语言·c++·算法
Jasmine_llq20 小时前
《P3157 [CQOI2011] 动态逆序对》
算法·cdq 分治·动态问题静态化+双向偏序统计·树状数组(高效统计元素大小关系·排序算法(预处理偏序和时间戳)·前缀和(合并单个贡献为总逆序对·动态问题静态化
爱吃rabbit的mq20 小时前
第09章:随机森林:集成学习的威力
算法·随机森林·集成学习
(❁´◡`❁)Jimmy(❁´◡`❁)21 小时前
Exgcd 学习笔记
笔记·学习·算法
YYuCChi21 小时前
代码随想录算法训练营第三十七天 | 52.携带研究材料(卡码网)、518.零钱兑换||、377.组合总和IV、57.爬楼梯(卡码网)
算法·动态规划
不能隔夜的咖喱1 天前
牛客网刷题(2)
java·开发语言·算法
VT.馒头1 天前
【力扣】2721. 并行执行异步函数
前端·javascript·算法·leetcode·typescript
进击的小头1 天前
实战案例:51单片机低功耗场景下的简易滤波实现
c语言·单片机·算法·51单片机
咖丨喱1 天前
IP校验和算法解析与实现
网络·tcp/ip·算法