当前位置: 首页 > news >正文

【Leetcode hot 100】437.路径总和 Ⅲ

问题链接

437.路径总和 Ⅲ

问题描述

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

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

示例 1:
在这里插入图片描述

输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出:3
解释:和等于 8 的路径有 3 条,如图所示。

示例 2:

输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:3

提示:

  • 二叉树的节点个数的范围是 [0,1000]
  • -10^9 <= Node.val <= 10^9
  • -1000 <= targetSum <= 1000

问题解答

解法一:暴力递归(基础思路)

思路
每个节点为起点,向下遍历所有可能的路径,计算路径和并统计符合条件的数量:

  1. 主函数:遍历二叉树的每个节点,对每个节点调用「路径统计辅助函数」;
  2. 辅助函数:从当前节点出发,递归遍历左/右子树,累加路径和,若等于 targetSum 则计数+1。

代码实现

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public int pathSum(TreeNode root, int targetSum) {if (root == null) {return 0;}// 1. 以当前节点为起点的路径数int currentCount = countPathFromNode(root, targetSum);// 2. 递归处理左子树(以左子树节点为起点)int leftCount = pathSum(root.left, targetSum);// 3. 递归处理右子树(以右子树节点为起点)int rightCount = pathSum(root.right, targetSum);// 总路径数 = 当前节点起点数 + 左子树起点数 + 右子树起点数return currentCount + leftCount + rightCount;}/*** 辅助函数:从当前节点出发,统计路径和等于 target 的数量* @param node 当前起点节点* @param target 剩余需要满足的路径和(每次累加节点值后递减)* @return 符合条件的路径数*/private int countPathFromNode(TreeNode node, long target) {if (node == null) {return 0;}// 记录当前节点是否满足路径和(若满足则+1)int count = 0;if (node.val == target) {count = 1;}// 递归遍历左子树:剩余目标和 = 原目标 - 当前节点值int left = countPathFromNode(node.left, target - node.val);// 递归遍历右子树int right = countPathFromNode(node.right, target - node.val);// 总路径数 = 当前节点满足数 + 左子树满足数 + 右子树满足数return count + left + right;}
}

复杂度分析

  • 时间复杂度:O(n²),n 为二叉树节点数。每个节点作为起点需遍历其下方所有节点,最坏情况(链状树)下总操作数为 n + (n-1) + ... + 1 = O(n²)
  • 空间复杂度:O(n),递归栈深度取决于树的高度,最坏情况(链状树)为 O(n)。

解法二:前缀和 + 哈希表(优化思路)

思路
利用「前缀和」优化时间复杂度,核心原理:

  • 前缀和定义:从根节点到当前节点的路径上所有节点值的总和,记为 currSum
  • 路径和推导:若存在某一前驱节点的前缀和 prevSum,使得 currSum - prevSum = targetSum,则从「前驱节点的下一个节点」到「当前节点」的路径和等于 targetSum
  • 哈希表作用:存储「前缀和 → 出现次数」,快速查询 prevSum = currSum - targetSum 的出现次数,避免重复遍历。

关键细节

  1. 初始状态:哈希表需先放入 (0, 1),表示「前缀和为 0 的路径出现 1 次」(对应从根节点开始的路径,如 currSum = targetSum 时,prevSum = 0 需计数);
  2. 回溯操作:深度优先遍历(DFS)后需「撤销」当前前缀和的计数(次数-1,若次数为 0 则移除),避免影响其他路径的计算;
  3. 溢出处理:用 long 存储前缀和,避免节点值累加时超出 int 范围。

代码实现

import java.util.HashMap;
import java.util.Map;class Solution {public int pathSum(TreeNode root, int targetSum) {// 哈希表:key=前缀和,value=该前缀和出现的次数Map<Long, Integer> prefixSumMap = new HashMap<>();// 初始状态:前缀和为 0 的路径出现 1 次prefixSumMap.put(0L, 1);// 调用 DFS 递归统计路径数return dfs(root, 0L, targetSum, prefixSumMap);}/*** DFS 递归函数:统计以当前节点为终点的符合条件的路径数* @param node 当前遍历节点* @param currSum 从根节点到当前节点的前缀和* @param targetSum 目标路径和* @param prefixSumMap 前缀和哈希表* @return 符合条件的路径数*/private int dfs(TreeNode node, long currSum, int targetSum, Map<Long, Integer> prefixSumMap) {// 递归终止条件:节点为空,无路径if (node == null) {return 0;}// 1. 计算当前节点的前缀和(累加当前节点值)currSum += node.val;// 2. 统计符合条件的路径数:查询 prevSum = currSum - targetSum 的出现次数int count = prefixSumMap.getOrDefault(currSum - targetSum, 0);// 3. 将当前前缀和加入哈希表(次数+1)prefixSumMap.put(currSum, prefixSumMap.getOrDefault(currSum, 0) + 1);// 4. 递归遍历左子树和右子树,累加路径数count += dfs(node.left, currSum, targetSum, prefixSumMap);count += dfs(node.right, currSum, targetSum, prefixSumMap);// 5. 回溯:移除当前前缀和的计数(避免影响其他路径)prefixSumMap.put(currSum, prefixSumMap.get(currSum) - 1);// 若次数减为 0,可从哈希表中删除(优化空间,非必需)if (prefixSumMap.get(currSum) == 0) {prefixSumMap.remove(currSum);}// 返回当前节点及其子树的符合条件的路径数return count;}
}

复杂度分析

  • 时间复杂度:O(n),n 为二叉树节点数。每个节点仅遍历一次,哈希表的 putget 操作均为 O(1)。
  • 空间复杂度:O(n),哈希表存储的前缀和数量不超过节点数,递归栈深度取决于树的高度(最坏情况 O(n))。

两种解法对比

解法时间复杂度空间复杂度适用场景
暴力递归O(n²)O(n)树规模较小(n < 1000)
前缀和+哈希表O(n)O(n)树规模较大,追求高效解法
http://www.dtcms.com/a/391172.html

相关文章:

  • 神经网络学习笔记16——高效卷积神经网络架构汇总(SqueezeNet、MobileNet、ShuffleNet、EfficientNet、GhostNet)
  • 解码阳光电源技术壁垒:以IPD和数字化驱动模块化创新的研发体系
  • ARM体系结构—架构—指令集—寄存器—工作模式
  • 自适应全变分模型的图像平滑去噪与边缘保留算法
  • 主流前端框架比较
  • 前端接口参数序列化
  • 精细调光,稳定驱动:AP5165B 在低压LED照明中的卓越表现
  • EasyGBS如何实现企业园区视频监控一体化管理?
  • Ledit 16.3 版图软件全面系统性教程
  • Linux的DTS配置信息
  • 线程池全面解析:核心原理、参数配置与实践指南
  • 【Linux】自定义协议——网络计算器实现
  • Ubuntu 安装的docker-compose拉取镜像失败问题处理办法
  • 第35篇:AI前沿:具身智能(Embodied AI)与通用人工智能(AGI)
  • LangChain 入门到精通企业项目实践之 LangChain 聊天模型
  • crush情感分析项目01
  • 免费插件分享 | Missing References Search
  • ECU OTA测试
  • Jenkins运维之路(Slave容器节点)
  • Amazon Lambda + API Gateway 实战,无服务器架构入门
  • 芯片管脚的源电流与漏电流
  • Django+ARIMA微博舆情预警系统 SnowNLP情感分析 Echarts可视化 机器学习 大数据项目✅
  • SIMetrix 8.30仿真蓝牙天线上的无源滤波器
  • [x-cmd] 升级 x-cmd 指南
  • AXI4-Stream总线流控握手实战经验总结
  • RAWSim-O-main项目Trae解析
  • react固定容器标签超出n+展示
  • ​​HarmonyOS应用开发:从入门到实战的完整指南​
  • QT与GTK生态最新进展及特性对比(2025年)
  • 包管理器分析