零 标题:算法(leetcode,附思维导图 + 全部解法)300题之(124)二叉树中的最大路径和
一 题目描述
二 解法总览(思维导图)
三 全部解法
面试官:题目看得差不多了吧,怎么样、有无思路呀?
狂徒张三:对于二叉树的题目,我们可以优先考虑使用【递归】。
面试官:哦?这是为什么呢?
狂徒张三:生物上书上有讲 —— “结构与功能相适应”,那我认为“算法”里,也有类似的东西,如 “数据结构与算法相适应” —— 仔细观察,你会发现,当前根节点、其左右节点的结构是 “同构” 的,那那么我们自然会联想到使用 “递归” 。
面试官:
狂徒张三:
面试官(一脸严肃):
旁白:过了4分41秒,张三便齐刷刷的写完了如下代码。
1 方案1
1)代码:
// 方案1 “就地更新 - 再次遍历法(自己)”。
// 技巧:二叉树的题目应优先考虑使用递归。
// 思路:
// 1)状态初始化:resVal = Number.NEGATIVE_INFINITY (因 要求最大值,故 先置为 最大的负数值 )
// 2)核心1:根据情况,更新 树上各节点的值 。
// 3)核心2:遍历新树,更新最大值 resVal 。
// 4)返回结果 resVal 。
var maxPathSum = function(root) {
// 根据情况,更新 树上各节点的值 。
const updateTree = (root = null) => {
// 1.1)递归出口1
if (!root) {
return;
}
// 1.2)递归出口2
let {left, right, val} = root;
if ((!left) && (!right)) {
return;
}
// 2)递归主体
// 2.1)更新 左子树的值 。
updateTree(left);
// 2.2)更新 右子树的值 。
updateTree(right);
// 2.3)更新 根节点的值 。
// 分3种情况:带上左子树的值 或 带上右子树的值 或 左、右子树值均不带。
root.val = val + Math.max((left?.val || 0), (right?.val || 0), 0);
// 2.4)更新结果值 resVal 。
resVal = Math.max(resVal, val + (left?.val || 0) + (right?.val || 0));
};
// 遍历新树,更新最大值 resVal 。
const getMaxValByNewTree = (root = null) => {
// 1)递归出口
if (!root) {
return;
}
// 2)递归主体
const {left, right, val} = root;
// 2.1)根节点
resVal = Math.max(resVal, val);
// 2.2)左子树
getMaxValByNewTree(left);
// 2.3)右子树
getMaxValByNewTree(right);
};
// 1)状态初始化:resVal = Number.NEGATIVE_INFINITY (因 要求最大值,故 先置为 最大的负数值 )
let resVal = Number.NEGATIVE_INFINITY;
// 2)核心1:根据情况,更新 树上各节点的值 。
updateTree(root);
// 3)核心2:遍历新树,更新最大值 resVal 。
getMaxValByNewTree(root);
// 4)返回结果 resVal 。
return resVal;
};
狂徒张三:怎么样,还算过关吧?
面试官:一脸不屑。
狂徒张三:满脸自信。
面试官:你仔细看下你这个解法,是不是遍历了2次?那么他们这2次遍历分别做了什么?我们是否可以只进行1次遍历 —— 即进行遍历的合并?
旁白:面对面试官的一键3问(“暗示一键三连??”),张直接 “吓出了3滴冷汗”。
狂徒张三(面色略略发白,并仔仔细细的看了一遍代码。恍然大悟道):
1、确实遍历了2次。
2、分了做了如下工作:
1)updateTree:遍历树,当前节点值从左、右子树的值和0中选出最大值,并加到当前根节点的值上。并 更新结果值 resVal (因为此时的 resVal 有可能是 有当前根节点、当前左、右子树一起组成的路径值之和)。
2)getMaxValByNewTree:更新结果值 resVal(因为此时的 resVal 有可能是在当前根节点取得的)。
3、看起来,updateTree、getMaxValByNewTree中产生的操作可以合并到一起。
旁边:不一会(过了4分11秒),张三便基于方案1和新思路写完了如下的代码。
2 方案2
1)代码:
// 方案2 “官方,递归法。(本质:跟方案1的思路是一致的,只是少了1次树的遍历 —— getMaxValByNewTree)”。
// 参考:
// 1)https://leetcode-cn.com/problems/binary-tree-maximum-path-sum/solution/er-cha-shu-zhong-de-zui-da-lu-jing-he-by-leetcode-/
// 思路:
// 1)状态初始化:resVal = Number.NEGATIVE_INFINITY
// (因 要求最大值,故 先置为 最大的负数值 )。
// 2)核心:遍历新树,更新最大值 resVal 。
// 3)返回结果 resVal 。
var maxPathSum = function(root) {
// 核心:递归处理!
const maxGain = (root = null) => {
// 1)递归出口
if (!root) {
return 0;
}
// 2)递归主体
// 2.1)递归计算左右子节点的最大贡献值
// 只有在最大贡献值大于 0 时,才会选取对应子节点
const leftGain = Math.max(maxGain(root.left), 0),
rightGain = Math.max(maxGain(root.right), 0),
// 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值
priceNewpath = root.val + leftGain + rightGain;
// 2.2)更新结果值 resVal 。
resVal = Math.max(resVal, priceNewpath);
// 2.3)返回当前根节点的最大贡献值。
return root.val + Math.max(leftGain, rightGain);
}
// 1)状态初始化:resVal = Number.NEGATIVE_INFINITY
// (因 要求最大值,故 先置为 最大的负数值 )。
let resVal = Number.NEGATIVE_INFINITY;
// 2)核心:遍历新树,更新最大值 resVal 。
maxGain(root);
// 3)返回结果 resVal 。
return resVal;
}
狂徒张三(写完了如上代码,急忙问面试官):这算是最优解,算是通过面试了吧?
面试官:孺子可教也~
狂徒张三:
又1个offer,然后马上就要出任 CEO 了,我晚上应该是去吃 沙县小吃 呢? 还是 兰州拉面 呢?哎,选择太多也是一种烦恼!
四 资源分享 & 更多
1 历史文章 - 总览
2 博主简介
码农三少 ,一个致力于编写 极简、但齐全题解(算法) 的博主。
专注于 一题多解、结构化思维 ,欢迎一起刷穿 LeetCode ~