精读《算法题 - 二叉树中的最大路径和》

news2024/11/25 14:48:03

今天我们看一道 leetcode hard 难度题目:二叉树中的最大路径和。

题目

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

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

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

示例1:

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

思考

第一想法是,这道题不安常理出牌,因为路径竟然不是自上而下的,而是可以横向蛇形游走的,如下图:

e1f17629e100310a9dee7cbbab975e03.png

尝试动态规划

第二想法是,这种蛇形游走的路径,求路径最大值应该用什么方法?大概率是暴力解法,因为 必须遍历完所有节点,才知道是否有更大的值的可能性,而应对暴力解法最好的策略是动态规划,那么应该如何定义状态?经过一番思考,二叉树点到点之间仅有唯一一条路径,如果我们能枚举计算经过每个点的所有可能路径的最大值,那么找到其中最大的就可以得到答案。但可惜的是,以 “点” 为变量没办法写转移方程。

以暴力解法为基础思考

此时要切换想法,经过一些思考,我决定以正序角度模拟一下寻找最大路径和的思路:首先选择一个起点,找到以该起点开始的最大路径合。那么从该起点就有最多 3 种走法,分别是向根节点走、左子节点、右子节点走:

2cbe8500fde5cb4e97f8dae6ea8c712c.png

最暴力的解法是遍历每个点,把所有方向都走一遍,找到所有可能的最大值。 这无疑是一个最有效的兜底解法,但效率太低,那么为了提升效率,假设一条路径的最大潜力已经计算过一次了,那么一条新路径经过时,就没必要重新算一遍。所以我们要寻找每个方向的最大贡献

寻找每个方向的最大贡献

假设我们提前找到了经过每个点的最大贡献如下:

dcc6354eb3a803f7f584d05a7869b01e.png

根节点的最大贡献 10 的含义为:从 3 向根节点走,所有可能路径能带来的最大正数收益为 10。所以此时最大路径和显然为:5 + 3 + 10 = 18.

但此时矛盾来了,根节点的最大贡献 10 是从 3 向根节点走的角度定义的,它有两个致命问题:

  1. 每个节点的最大贡献最好只能有一个数字,依赖方向的话复杂度太高了。

  2. 如果要依赖方向,那么从根节点右子节点走向根节点的最大贡献,其实依赖从左子结点出发的最大贡献,相互死锁了。

这种最大贡献几乎不可能找到,再花时间思考只是浪费时间,所以我们要改变策略了。再想想二叉树的特征是什么,怎么样能最稳定的定义每个节点的最大贡献?很容易想到的是以树的深度来定义,即 以当前节点向子节点遍历时,能带来的最大贡献。这种最大贡献是比较容易计算的。

每个子树的最大贡献

b46decadc35a7b5f81c851afa43e192b.png

如上图所示,以 8 这个节点的子树,假设通过一系列递归找到,它能提供的最大贡献就是 8,且这个贡献必须是一条没有分叉的线,这样这个最大贡献对于它的父节点才有意义,即父节点可以把这个节点连上,形成一条更长的没有分叉的线。如果子线都有分叉,整条线就会存在分叉,就不符合题意了。

这个 8 很容易计算,从叶子结点向上推,找到最大且大于 0 的子节点连成线即可。

但回到这道题,如果我们仅仅计算了每个点所在子树的最大贡献,那么其最大值仅是垂直的线中的最大值,没有考虑到该题路径可以横向蛇形游走的特性:

8cda1d92a8b258f903d7254f217429fd.png

如上图所示,红色的数字为以该点开始的子树的最大贡献,那么根节点 32 其实就是红色路径提供的路径和,对于纵向走位来说是最大的,但并不是本题最大的。本题最大的值,还得把下图红色的路径考虑上,变成一个横向的线,此时最大值达到了 32 + 8 = 40:

aca86ba40188adacd7cf3f8fc4223679.png

但其实要把线变成横向的,也仅需要多考虑另一个子节点而已,因为所有子树的最大贡献已经提前算好,根本无需再深入子子节点。也就是说,在计算最大路径和时(重要内容字体加粗!):

  1. 经过该点的最大路径和,要同时考虑该点 + 左右子树最大贡献,也就是此时路径会形成类似倒扣的 U 型。

  2. 但该节点的最大贡献呢,只能考虑该点 + 左 or 右子树最大贡献的,不能形成倒扣的 U 型,因为这个最大贡献需要被其父节点作第 1 条规则时考虑,如果此时已经是倒扣 U 型了,那么父节点再分叉一次倒扣的 U 型,就不是一条线了,可能会形成如下图所示奇怪的形状:

e4c6dccb2a2e424dd42f420c63bfc48c.png

这就是本题最精彩的思考点。

代码实现

想通了之后,代码就很简单了:

function maxPathSum(root: TreeNode | null): number {
  let maxValue = -Infinity

  function maxOneLinePathByNode(node: TreeNode): number {
    // 如果节点为空,返回负无穷,必然不会被最大路径和带上
    if (node === null) return -Infinity

    // 左子树最大贡献(如果为负数则为 0,表示不带上左子树)
    const leftChildMaxValue = Math.max(maxOneLinePathByNode(node.left), 0)
    // 右子树最大贡献 - 同理
    const rightChildMaxValue = Math.max(maxOneLinePathByNode(node.right), 0)

    // 经过该点的最大路径和
    const currentPointMaxValue = node.val + leftChildMaxValue + rightChildMaxValue
    // 刷新 maxValue
    maxValue = Math.max(maxValue, currentPointMaxValue)

    // 返回不分叉的子树最大贡献
    return node.val + Math.max(leftChildMaxValue, rightChildMaxValue)
  }

  maxOneLinePathByNode(root)

  return maxValue
};

因为从根节点开始递归,可以算出所有子树的最大贡献,把经过每一个点的路径都考虑到了,所以答案是不重不漏的。

总结

该题有两个难点:

  1. 找到子树最大贡献思考方向。

  2. 子树最大贡献与最大路径和的计算方式稍有不同,需要分别处理。

最后,在从根节点递归寻找子树最大贡献时,就可以顺便计算出最大路径和,一定程度上是 “目标的副产物”,甚至可以怀疑该题是在思考子树最大贡献时,逆向推导出来的副产物。另一方面,也说明了子树最大贡献的重要性,它的一个衍生计算就可以是一道 hard 题。

讨论地址是:精读《算法 - 二叉树中的最大路径和》· Issue #504 · dt-fe/weekly

如果你想参与讨论,请 点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。

版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证)

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

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

相关文章

Si4010 一款带有MCU SoC RF发射机芯片 无线遥控器

Si4010是一款完全集成的SoC RF发射机,带有嵌入式CIP-51 8051 MCU,专为1GHz以下ISM频带设计。该芯片针对电池供电的应用进行了优化,工作电压为1.8至3.6 V,待机电流小于10 nA的超低电流消耗。高功率放大器可提供高达10 dBm的输出功率…

服务器数据库中了elbie勒索病毒怎么办,elbie勒索病毒解密,数据恢复

网络技术的不断成熟,为企业的生产运营提供了强有力的支撑,但是,随之而来的网络安全威胁也不断增加。云天数据恢复中心陆陆续续接到很多企业的求助,企业的服务器数据库e遭到了elbie勒索病毒攻击,导致企业计算机系统瘫痪…

基于PHP语言的会员系统搭建(Docker版)

1、操作系统 ubuntu22 2、安装Docker # Add Dockers official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -…

【UART】UART QA

UART常见知识点整理 定义:Universal Asynchronous Receiver/Transmitter - 通用异步收发传输器。 特点:速率不快、可全双工、结构上一般由波特率产生器、UART发送器、UART接收器组成,硬件2-3线。 线:RXD,TXD&#xff0…

cordova Xcode打包ios以及发布流程(ionic3适用)

第一步 1、申请iOS证书 2、导入证书到钥匙串 第二步 1、xcode配置iOS证书 1.1用Xcode打开你的项目(我的Xcode版本是新版) 修改如下图 回到基本信息设置界面,Bundie 这项填写,最先创建的那个appid,跟创建iOS描述文件时选…

操作系统——文件空闲存储的空间管理(王道视频p64)

1.总体概述: 2.存储空间初始化: 3.空闲表法 4.空闲链: (1)空闲“盘块链”: (2)空闲“盘区链”: 5.位视图法: (1)基本结构&#xff1…

Leetcode刷题详解——二叉树剪枝

1. 题目链接:814. 二叉树剪枝 2. 题目描述: 给你二叉树的根结点 root ,此外树的每个结点的值要么是 0 ,要么是 1 。 返回移除了所有不包含 1 的子树的原二叉树。 节点 node 的子树为 node 本身加上所有 node 的后代。 示例 1&…

【Linux】进程管理命令,了解运维的五大性能监测

目录 一、程序与进程 1、程序是什么? 2、进程是什么?线程是什么?服务是什么? 3、进程的特点 4、进程使用内存的问题 二、运维相关命令学习 命令① ps 静态显示系统的进程 第一种静态查看进程信息:ps aux 第二…

解决若依Ruoyi 插入数据返回1,实现主键回填,返回主键ID

最开始的时候ruoyi 插入数据会返回1&#xff0c;开始以为是id&#xff0c;后来发现返回的逻辑是 0失败&#xff0c;1成功。 即便他利用mybatis设置了如下, useGeneratedKeys"true" keyProperty"id"​​​​​​​<selectKey></selectKey> 如…

【Redis】Redis整合SSMRedis中的缓存穿透、雪崩、击穿的原因以及解决方案(详解)

目录&#xff1a; 目录 一&#xff0c;SSM整合redis 二&#xff0c;redis注解式缓存 三&#xff0c;Redis中的缓存穿透、雪崩、击穿的原因以及解决方案&#xff08;附图&#xff09; 一&#xff0c;SSM整合redis 1.原因&#xff1a; 整合SSM和Redis可以提升系统的性能、可…

【迁移ORACLE数据到MogDB/openGauss时的字符集问题】

一、问题概述 ORACLE数据库在存储数据的时候&#xff0c;有时候会存在这样一种现象&#xff0c;一张表里的数据&#xff0c;既存在UTF8字符的&#xff0c;也存在GBK字符的&#xff0c;同时还有可能存在乱码数据。 NLS_CHARACTERSET是数据库字符集&#xff0c;NLS_NCHAR_CHARAC…

一键搞定!多个模型结果快速合并成一个三线表,model1、model2、model3。。。

欢迎参加郑老师2023年孟德尔随机化课程即将开始 发表文章后退款&#xff01;郑老师科研统计课程详情 在观察性研究中&#xff0c;我们经常同时构建多个统计模型&#xff0c;不同的模型放入不同的协变量&#xff0c;从零个的单因素回归分析&#xff0c;到多个协变量的回归模型。…

jenkins结合k8s部署动态slave

1、完成k8s连接 在完成jenkins的部署后现安装kubernets的插件 如果jenkins 是部署在k8s集群中只需要填写一下 如果是非本集群的部署则需要填写证书等 cat ./config echo ‘certificate-authority-data-value’ | base64 -d > ./ca.crt echo ‘client-certificate-data’ |…

MySQL 排序,分组,Limit的优化策略

目录 1. MySQL 中的两种排序方式 2. 排序优化策略 2.1 对排序字段添加索引 2.2 可以和WHERT字段创建联合索引 2.3 优化 FilerSort 排序方式 3. 分组优化策略 3.1 能WHERE不HAVING 3.2 减少ORDER BY&#xff0c;GROUP BY&#xff0c;DISTINCT 3.3 遵照最左前缀法则 4.…

python 之 正则表达式模块re

文章目录 findall例子&#xff1a;特点和注意事项&#xff1a; match示例&#xff1a;match 对象的方法和属性&#xff1a;注意事项&#xff1a; search示例&#xff1a;match 对象的方法和属性&#xff1a;注意事项&#xff1a; split示例&#xff1a;参数说明&#xff1a;注意…

民宿酒店服务预约小程序的作用

民宿往往是旅游者们前往某个城市感受风情常住的地方&#xff0c;也因此在景区或特定地方&#xff0c;总是不乏大小民宿品牌&#xff0c;但除了市场高需求外&#xff0c;商家们所遇的痛点也不少&#xff1a; 1、获客引流难 民宿生意虽然需求量高&#xff0c;但各家品牌众多&am…

Unity meta的一些常见属性

Unity会项目文件夹中的每个文件分配一个同名后缀为.meta的文件。 我们可以将meta文件理解不同文件之间的桥梁&#xff0c;通过它引擎可以管理不同文件之间的依赖关系。 使用TXT文本文件打开之后&#xff0c;大致属性如下&#xff1a; 其中常用的属性有guid、 assetBundleName以…

大模型应用于数字人

大模型会改变整个软件行业&#xff0c; 其中具有代表性的产品之一是数字人&#xff0c; 那么&#xff0c;什么是数字人呢&#xff1f;数字人涉及了哪些关键技术呢&#xff1f;大模型对数字人的发展带来哪些影响呢&#xff1f; 1. 什么数字人&#xff1f; 数字人目前还缺乏一个相…

【图像分类】【深度学习】【Pytorch版本】AlexNet模型算法详解

【图像分类】【深度学习】【Pytorch版本】AlexNet模型算法详解 文章目录 【图像分类】【深度学习】【Pytorch版本】AlexNet模型算法详解前言AlexNet讲解卷积层的作用卷积过程特征图的大小计算公式Dropout的作用AlexNet模型结构 AlexNet Pytorch代码完整代码总结 前言 AlexNet是…

2.数制与编码

目录 一. 进位计数制 &#xff08;1&#xff09;二进制&#xff0c;八进制&#xff0c;十进制&#xff0c;十六进制 &#xff08;2&#xff09;二进制&#xff0c;八进制&#xff0c;十六进制的转换 &#xff08;3&#xff09;十进制转换成任意进制 &#xff08;4&#xf…