LeetCode二叉树路径和专题:最大路径和与路径总和计数的策略

news2025/4/24 9:34:11

目录

437. 路径总和 III

深度优先遍历

前缀和优化

124. 二叉树中的最大路径和 


437. 路径总和 III

给定一个二叉树的根节点 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]
  • -109 <= Node.val <= 109 
  • -1000 <= targetSum <= 1000 

深度优先遍历

遍历每个节点,以每个节点为根节点统计所有路径和为targetSum情况

class Solution {
    public int pathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return 0;
        }

        int ret = rootSum(root, targetSum);
        ret += pathSum(root.left, targetSum);
        ret += pathSum(root.right, targetSum);
        return ret;
    }

    public int rootSum(TreeNode root, int targetSum) {
        int ret = 0;

        if (root == null) {
            return 0;
        }
        int val = root.val;
        if (val == targetSum) {
            ret++;
        } 

        ret += rootSum(root.left, targetSum - val);
        ret += rootSum(root.right, targetSum - val);
        return ret;
    }
}

前缀和优化

解法一中应该存在许多重复计算,我们现在定义:前缀和是一个数列的每一项索引从0开始,表示从第0项到当前项的和。在这个问题中,我们将前缀和应用于二叉树的路径上,即从根节点到当前节点的所有节点值的和。

  1. 初始化:创建一个哈希表 prefix 来保存前缀和及其出现次数。为了处理从根节点开始的路径,我们提前在哈希表中设置前缀和0的计数为1。

  2. 递归遍历:使用深度优先搜索遍历二叉树。在每个节点处,计算从根节点到当前节点的前缀和。

  3. 查找当前路径和:检查当前的前缀和减去 targetSum 的结果是否在之前的路径中已经出现过,也就是检查 curr - targetSum 是否在哈希表 prefix 中。如果是,说明存在一个子路径的和等于 targetSum,将对应的次数添加到结果中。

  4. 更新哈希表:在访问节点之前,将当前前缀和的计数增加1,以表示这个前缀和现在被包含在路径中。

  5. 递归子节点:继续对左右子节点进行相同的处理。

  6. 回溯:在返回之前,需要将当前节点的前缀和的计数减1,因为当前节点即将被回溯,不应该计入其他路径(不存在以某个节点为根节点的路径所以要回溯避免影响其他路径)

class Solution {
    // 主函数
    public int pathSum(TreeNode root, int targetSum) {
        // 哈希表用于存储所有前缀和及其出现次数
        Map<Long, Integer> prefix = new HashMap<Long, Integer>();
        // 初始化:前缀和为0的路径有1条(空路径)
        prefix.put(0L, 1);
        // 开始深度优先搜索
        return dfs(root, prefix, 0, targetSum);
    }

    // 辅助函数:深度优先搜索
    public int dfs(TreeNode root, Map<Long, Integer> prefix, long curr, int targetSum) {
        // 如果当前节点为空,则返回0,表示没有路径
        if (root == null) {
            return 0;
        }

        int ret = 0; // 用于记录路径数
        curr += root.val; // 更新当前路径和

        // 查找当前路径和减去目标值的结果是否在前缀和中出现过
        // 出现过则表示找到了一条路径
        ret = prefix.getOrDefault(curr - targetSum, 0);

        // 更新前缀和中当前路径和的计数
        prefix.put(curr, prefix.getOrDefault(curr, 0) + 1);

        // 递归地搜索左子树和右子树,并累加路径数
        ret += dfs(root.left, prefix, curr, targetSum);
        ret += dfs(root.right, prefix, curr, targetSum);

        // 回溯:在返回之前,将当前节点的路径和的计数减1
        // 因为当前节点即将被回溯,不应该计入其他路径
        prefix.put(curr, prefix.getOrDefault(curr, 0) - 1);

        // 返回找到的路径总数
        return ret;
    }
}

让我们通过一个具体的例子来说明回溯在前缀和算法中的应用。假设有以下二叉树:

        10
       /  \
      5   -3
     / \    \
    3   2   11
   / \   \
  3  -2   1

并且我们要找的 targetSum 是8。我们将按照前缀和算法遍历这棵树。

  1. 开始于根节点 (10):

    • 当前前缀和: curr = 10
    • prefix = { (0,1) } (初始化,路径和为0出现了1次)
  2. 移动到左子节点 (5):

    • 当前前缀和: curr = 15
    • 更新前缀和计数: prefix = { (0,1), (15,1) }
  3. 继续到该节点的左子节点 (3):

    • 当前前缀和: curr = 18
    • 更新前缀和计数: prefix = { (0,1), (15,1), (18,1) }
  4. 进一步到该节点的左子节点 (3):

    • 当前前缀和: curr = 21
    • 更新前缀和计数: prefix = { (0,1), (15,1), (18,1), (21,1) }
  5. 回溯到节点 (3) 父节点 (3):

    • 我们完成了节点 (3) 的所有子节点的遍历
    • 当前前缀和: curr = 18
    • 回溯prefix = { (0,1), (15,1), (18,1) },移除之前节点 (3) 的前缀和
  6. 遍历节点 (3) 的右子节点 (-2):

    • 当前前缀和: curr = 16
    • 更新前缀和计数: prefix = { (0,1), (15,1), (18,1), (16,1) }
    • 回溯prefix = { (0,1), (15,1), (18,1) },完成节点 (-2) 的遍历,移除它的前缀和
  7. 回溯到节点 (5) 并转向它的右子节点 (2):

    • 我们完成了节点 (5) 的左子树的遍历
    • 当前前缀和: curr = 15
    • 回溯prefix = { (0,1), (15,1) },移除之前节点 (3) 和 (-2) 的前缀和
    • 当前前缀和: curr = 17 (添加节点 (2))
    • 更新前缀和计数: prefix = { (0,1), (15,1), (17,1) }
    • 检查17 (当前前缀和) - 8 (targetSum) = 9,在 prefix 中没有 9,所以没有发现新路径
    • 遍历节点 (2) 的左子节点 (1)
    • 当前前缀和: curr = 18 (添加节点 (1))
    • 更新前缀和计数: prefix = { (0,1), (15,1), (17,1), (18,1) }
    • 检查18 (当前前缀和) - 8 (targetSum) = 10,在 prefix 中有 10(根节点的值),所以我们找到了一条路径: 10 → 5 → 2 → 1
    • 回溯:完成节点 (1) 的遍历,回溯节点 (2)
  8. 最终回溯到根节点 (10) 并转向它的右子节点 (-3):

    • 当前前缀和: curr = 10
    • 回溯prefix = { (0,1) },移除左子树的前

124. 二叉树中的最大路径和 

二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和 。

示例 1:

输入:root = [1,2,3]
输出:6
解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6

示例 2:

输入:root = [-10,9,20,null,null,15,7]
输出:42
解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42

提示:

  • 树中节点数目范围是 [1, 3 * 104]
  • -1000 <= Node.val <= 1000
class Solution {
    int maxSum = Integer.MIN_VALUE;

    public int maxPathSum(TreeNode root) {
        maxGain(root);
        return maxSum;
    }

    public int maxGain(TreeNode node) {
        if (node == null) {
            return 0;
        }
        
        // 递归计算左右子节点的最大贡献值
        // 只有在最大贡献值大于 0 时,才会选取对应子节点
        int leftGain = Math.max(maxGain(node.left), 0);
        int rightGain = Math.max(maxGain(node.right), 0);

        // 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值
        int priceNewpath = node.val + leftGain + rightGain;

        // 更新答案
        maxSum = Math.max(maxSum, priceNewpath);

        // 返回节点的最大贡献值
        return node.val + Math.max(leftGain, rightGain);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1345639.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MYSQL】-函数

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

在线智能防雷监控检测系统应用方案

在线智能防雷监控检测系统是一种利用现代信息技术&#xff0c;对防雷设施的运行状态进行实时监测、管理和控制的系统&#xff0c;它可以有效提高防雷保护的安全性、可靠性和智能化程度&#xff0c;降低运维成本和风险&#xff0c;为用户提供全方位的防雷解决方案。 地凯科技在…

Vue常见面试问答

vue响应式数据 vue2 Vue2 的对象数据是通过 Object.defineProperty 对每个属性进行监听&#xff0c;当对属性进行读取的时候&#xff0c;就会触发 getter&#xff0c;对属性进行设置的时候&#xff0c;就会触发 setter。 /** * 这里的函数 defineReactive 用来对 Object.def…

2023.12.28 关于 Redis 数据类型 List 内部编码、应用场景

目录 List 编码方式 早期版本 现今版本 List 实际应用 多表之间的关联关系 消息队列 频道&#xff08;多列表&#xff09;消息队列 微博 Timeline 栈 & 队列 List 编码方式 早期版本 早期版本 List 类型的内部编码方式有两种 ziplist&#xff08;压缩列表&#xf…

论文阅读<Contrastive Learning-based Robust Object Detection under Smoky Conditions>

论文链接&#xff1a;https://openaccess.thecvf.com/content/CVPR2022W/UG2/papers/Wu_Contrastive_Learning-Based_Robust_Object_Detection_Under_Smoky_Conditions_CVPRW_2022_paper.pdf Abstract 目标检测是指有效地找出图像中感兴趣的目标&#xff0c;然后准确地确定它们…

ssrf之gopher协议的使用和配置,以及需要注意的细节

gopher协议 目录 gopher协议 &#xff08;1&#xff09;安装一个cn &#xff08;2&#xff09;使用Gopher协议发送一个请求&#xff0c;环境为&#xff1a;nc起一个监听&#xff0c;curl发送gopher请求 &#xff08;3&#xff09;使用curl发送http请求&#xff0c;命令为 …

【MATLAB】PSO粒子群优化LSTM(PSO_LSTM)的时间序列预测

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 PSO粒子群优化LSTM&#xff08;PSO-LSTM&#xff09;是一种将粒子群优化算法&#xff08;PSO&#xff09;与长短期记忆神经网络&#xff08;LSTM&#xff09;相结合的混合模型。该算法通过…

Kubernetes技术与架构-集群管理

Kubernetes技术与架构提供支撑工具支持集群的规划、安装、创建以及管理。 数字证书 用户可以使用easyrsa、openssl、cfssl工具生成数字证书&#xff0c;在kubernetes集群的api server中部署数字证书用于访问鉴权 资源管理 如上所示&#xff0c;定义一个服务类service用于负…

计算机视觉技术-目标检测数据集

目标检测领域没有像MNIST和Fashion-MNIST那样的小数据集。 为了快速测试目标检测模型&#xff0c;我们收集并标记了一个小型数据集。 首先&#xff0c;我们拍摄了一组香蕉的照片&#xff0c;并生成了1000张不同角度和大小的香蕉图像。 然后&#xff0c;我们在一些背景图片的随机…

【计算机网络】第五,六章摘要重点

1.运输层协议概述 运输层提供的是进程之间的通信 2. 3.套接字指的是什么 ip地址端口号 4.每一条TCP语句唯一地被通信两端连接的两个端点 5.TCP传输如何实现 以字节为单位的滑动窗口 超时重传 选择确认 6.TCP流量控制和拥塞控制的区别 流量控制&#xff1a;点对点通信…

android 13.0 Launcher3长按app弹窗设置为圆角背景功能实现一

1.前言 在13.0的系统ROM定制化开发中,在进行一些Launcher3的定制化开发中,在使用app的弹窗的功能时,会弹出应用信息和微件之类的内容,所以在定制需求中,需要默认设置为圆角背景,接下来就来分析下相关功能的实现 如图: 2.Launcher3长按app弹窗设置为圆角背景功能实现的核…

如何高效查询文件:Linux 下的多种方法详解

如何高效查询文件&#xff1a;Linux 下的多种方法详解 在日常工作中&#xff0c;我们经常需要查找文件&#xff0c;无论是寻找特定的代码文件、配置文件还是其他文档。Linux 提供了多种强大的命令和工具&#xff0c;通过巧妙地使用管道符&#xff0c;我们可以将这些命令组合起来…

2023年03月17日_微软和谷歌办公AI的感慨

2023年3月17日 最近这个科技圈的消息 有点爆炸的让人应接不暇了 各种大公司简直就是神仙打架 你从来没有见过这么密集的 这么高频的产品发布 昨天微软是发布了Office 365 Copilot 在里边提供了大量的AI的功能 然后谷歌呢也发布了这个Google Workspace AI 也是跟365 Cop…

学生数据可视化与分析工具 vue3+flask实现

目录 一、技术栈亮点 二、功能特点 三、应用场景 四、结语 学生数据可视化与分析工具介绍 在当今的教育领域&#xff0c;数据驱动的决策正变得越来越重要。为了满足学校、教师和学生对于数据深度洞察的需求&#xff0c;我们推出了一款基于Vue3和Flask编写的学生数据可视化…

利用idea+ jclasslib插件查看和分析 Java 类文件的字节码

jclasslib介绍 jclasslib 插件是一个用于 IntelliJ IDEA 的工具&#xff0c;它允许开发者在集成开发环境&#xff08;IDE&#xff09;内直接查看和分析 Java 类文件的字节码。这个插件尤其对于想要深入了解 Java 字节码、类加载机制、以及 Java 虚拟机&#xff08;JVM&#xf…

[Angular] 笔记 22:ElementRef

chatgpt: ElementRef 是 Angular 中的一个类&#xff0c;它用于包装对 DOM 元素的引用。它允许开发者直接访问与 Angular 组件关联的宿主 DOM 元素。 当在 Angular 中需要直接操作 DOM 元素时&#xff0c;可以使用 ElementRef。通常情况下&#xff0c;最好避免直接操作 DOM&a…

性能优化-如何提高cache命中率

本文主要介绍性能优化领域常见的cache的命中率问题&#xff0c;旨在全面的介绍提高cache命中率的方法&#xff0c;以供大家编写出性能友好的代码&#xff0c;并且可以应对性能优化领域的面试问题。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &am…

Grafana监控数据可视化

Grafana 是一个可视化面板&#xff0c;有着非常漂亮的图表和布局展示&#xff0c;功能齐全的度量仪表盘和图形编辑器&#xff0c;支持 Graphite、zabbix、InfluxDB、Prometheus、OpenTSDB、Elasticsearch 等作为数据源&#xff0c;比 Prometheus 自带的图表展示功能强大太多&am…

论文阅读——Slide-Transformer(cvpr2023)

Slide-Transformer: Hierarchical Vision Transformer with Local Self-Attention 一、分析 1、改进transformer的几个思路&#xff1a; &#xff08;1&#xff09;将全局感受野控制在较小区域&#xff0c;如&#xff1a;PVT&#xff0c;DAT&#xff0c;使用稀疏全局注意力来…

Mysql 容易忘的 sql 指令总结

目录 一、操作数据库的基本指令 二、查询语句的指令 1、基本查询语句 2、模糊查询 3、分支查询 4、 分组查询 5、分组查询 6、基本查询总结&#xff1a; 7、子查询 8、连接查询 三、MySQL中的常用函数 1、时间函数 2、字符串函数 3、聚合函数 4、运算函数 四、表…