今日主要总结一下,1373. 二叉搜索子树的最大键值和

题目:1373. 二叉搜索子树的最大键值和

Leetcode题目地址

题目描述:
给你一棵以 root 为根的 二叉树 ,请你返回 任意 二叉搜索子树的最大键值和。

二叉搜索树的定义如下:

任意节点的左子树中的键值都 小于 此节点的键值。
任意节点的右子树中的键值都 大于 此节点的键值。
任意节点的左子树和右子树都是二叉搜索树。

示例 1:

输入:root = [1,4,3,2,4,2,5,null,null,null,null,null,null,4,6]
输出:20
解释:键值为 3 的子树是和最大的二叉搜索树。
示例 2:

输入:root = [4,3,null,1,2]
输出:2
解释:键值为 2 的单节点子树是和最大的二叉搜索树。
示例 3:

输入:root = [-4,-2,-5]
输出:0
解释:所有节点键值都为负数,和最大的二叉搜索树为空。
示例 4:

输入:root = [2,1,3]
输出:6
示例 5:

输入:root = [5,4,8,3,null,6,3]
输出:7

提示:

每棵树有 1 到 40000 个节点。
每个节点的键值在 [-4 * 10^4 , 4 * 10^4] 之间。

本题重难点

二叉树相关题目最核心的思路是明确当前节点需要做的事情是什么。

现在我们想计算子树中 BST 的最大和,站在当前节点的视角,需要做什么呢?

1、首先我想知道以我为根的这棵树是不是 BST,所以我肯定得知道左右子树是不是合法的 BST。因为如果这俩儿子有一个不是 BST,以我为根的这棵树肯定不会是 BST 的,对吧。

2、如果左右子树都是合法的 BST,我得看一下左右子树加上自己还是不是合法的 BST 了。因为按照 BST 的定义,当前节点的值应该大于左子树的最大值,小于右子树的最小值,否则就破坏了 BST 的性质。

3、因为题目要计算最大的节点之和,如果左右子树加上我自己还是一棵合法的 BST,也就是说以我为根的整棵树是一棵 BST,那我需要知道我们这棵 BST 的所有节点值之和是多少,方便和别的 BST 子树进行比较!

根据以上三点,站在当前节点的视角,需要知道以下具体信息:

1、左右子树是否是 BST。

2、左子树的最大值和右子树的最小值。

3、左右子树的节点值之和。

只有知道了这几个值,我们才能满足题目的要求,后面我们会想方设法计算这些值。

现在可以尝试用伪码写出算法的大致逻辑:

一、初步构思伪代码

// 全局变量,记录最终结果
int maxSum = 0;
/* 计算以 root 为根的二叉树的最大值 */
int findMax(TreeNode root) {}/* 计算以 root 为根的二叉树的最小值 */
int findMin(TreeNode root) {}/* 计算以 root 为根的二叉树的节点和 */
int findSum(TreeNode root) {}/* 判断以 root 为根的二叉树是否是 BST */
boolean isBST(TreeNode root) {}/* 遍历二叉树 */
void traverse(TreeNode root) {if (root == null) {return;}/******* 前序遍历位置 *******/// 判断左右子树是不是 BSTif (!isBST(root.left) || !isBST(root.right)) {goto next;}// 计算左子树的最大值和右子树的最小值int leftMax = findMax(root.left);int rightMin = findMin(root.right);// 判断以 root 节点为根的树是不是 BSTif (root.val <= leftMax || root.val >= rightMin) {goto next;}// 如果条件都符合,计算当前 BST 的节点之和int leftSum = findSum(root.left);int rightSum = findSum(root.right);int rootSum = leftSum + rightSum + root.val;// 计算 BST 节点的最大和this.maxSum = Math.max(maxSum, rootSum);/**************************/// 递归左右子树next:traverse(root.left);traverse(root.right);
}/* 主函数 */
public int maxSumBST(TreeNode root) {traverse(root);return maxSum;
}

初步构思代码问题:

稍作分析就会发现,这几个辅助函数都是递归函数,都要遍历输入的二叉树,外加traverse函数本身的递归,可以说是递归上加递归,所以这个解法的复杂度是非常高的。

但是根据刚才的分析,像leftMax、rootSum这些变量又都得算出来,否则无法完成题目的要求。

我们希望既算出这些变量,又避免辅助函数带来的额外复杂度,鱼和熊掌全都要!

其实是可以的,只要把前序遍历变成后序遍历,让traverse函数把辅助函数做的事情顺便做掉。

其他代码不变,我们让traverse函数做一些计算任务,返回一个数组

// 全局变量,记录 BST 最大节点之和
int maxSum = 0;/* 主函数 */
public int maxSumBST(TreeNode root) {traverse(root);return maxSum;
}// 函数返回 int[]{ isBST, min, max, sum}
int[] traverse(TreeNode root) {int[] left = traverse(root.left);int[] right = traverse(root.right);/******* 后序遍历位置 *******/// 通过 left 和 right 推导返回值// 并且正确更新 maxSum 变量/**************************/
}

traverse(root)返回一个大小为 4 的 int 数组,我们暂且称它为res,其中:

res[0]记录以root为根的二叉树是否是 BST,若为 1 则说明是 BST,若为 0 则说明不是 BST;

res[1]记录以root为根的二叉树所有节点中的最小值;

res[2]记录以root为根的二叉树所有节点中的最大值;

res[3]记录以root为根的二叉树所有节点值之和。

其实这就是把之前分析中说到的几个值放到了res数组中,最重要的是,我们要试图通过left和right正确推导出res数组。

二、最终优化正确解法

C++代码

class Solution {public:int maxSum = 0;vector<int> traversal(TreeNode* node){if(!node){vector<int> tmp = {1, INT_MAX, INT_MIN, 0};return tmp;}vector<int> left = traversal(node->left);vector<int> right = traversal(node->right);vector<int> res(4, 0);if(left[0] == 1 && right[0] == 1 && node->val > left[2] && node->val < right[1]){res[0] = 1;res[1] = min(node->val, left[1]);res[2] = max(node->val, right[2]);res[3] = left[3] + right[3] + node->val;maxSum = max(maxSum, res[3]);}else res[0] = 0;return res;}int maxSumBST(TreeNode* root) {traversal(root);return maxSum;}
};

这样,这道题就解决了,traverse函数在遍历二叉树的同时顺便把之前辅助函数做的事情都做了,避免了在递归函数中调用递归函数,时间复杂度只有 O(N)。

你看,这就是后序遍历的妙用,相对前序遍历的解法,现在的解法不仅效率高,而且代码量少,比较优美。

如果当前节点要做的事情需要通过左右子树的计算结果推导出来,就要用到后序遍历。

因为以上几点都可以通过后序遍历的方式「顺便」计算出来,所以这道题使用后序遍历肯定是最高效的。

另外,我们要尽可能避免递归函数中调用其他递归函数,如果出现这种情况,大概率是代码实现有瑕疵,可以进行类似本文的优化来避免递归套递归。

总结

这道题的重难点首先是:明确当前节点需要做的事情是什么。
根据以上三点,站在当前节点的视角,需要知道以下具体信息:

1、左右子树是否是 BST。

2、左子树的最大值和右子树的最小值。

3、左右子树的节点值之和。

接着稍作分析就会发现,这几个辅助函数都是递归函数,都要遍历输入的二叉树,外加traverse函数本身的递归,可以说是递归上加递归,所以这个解法的复杂度是非常高的。

但是根据刚才的分析,像leftMax、rootSum这些变量又都得算出来,否则无法完成题目的要求。

我们希望既算出这些变量,又避免辅助函数带来的额外复杂度,鱼和熊掌全都要!

其实是可以的,只要把前序遍历变成后序遍历,让traverse函数把辅助函数做的事情顺便做掉。——这个是一个常见的巧妙算法思路!!!

其他代码不变,我们让traverse函数做一些计算任务,返回一个数组


欢迎大家扫码关注本人公众号:编程复盘与思考随笔

(关注后可以免费获得本人在csdn发布的资源源码)

公众号主要记录编程和刷题时的总结复盘笔记和心得!并且分享读书、工作、生活中的一些思考感悟!

LeetCode刷题复盘笔记—1373. 二叉搜索子树的最大键值和相关推荐

  1. [LeetCode 1373]二叉搜索子树的最大键值和

    题目描述 链接:[LeetCode 1373]二叉搜索子树的最大键值和 给你一棵以 root 为根的 二叉树 ,请你返回 任意 二叉搜索子树的最大键值和. 二叉搜索树的定义如下: 任意节点的左子树中的 ...

  2. LeetCode 第 1373 题:二叉搜索子树的最大键值和

    LeetCode 第 1373 题:二叉搜索子树的最大键值和 题目 1373. 二叉搜索子树的最大键值和 的要求是,给你一颗以 root 为根的二叉树,要求返回任意二叉搜索子树的最大键值和. 首先要注 ...

  3. Leetcode 1373:二叉搜索子树的最大键值和(超详细的解法!!!)

    给你一棵以 root 为根的 二叉树 ,请你返回 任意 二叉搜索子树的最大键值和. 二叉搜索树的定义如下: 任意节点的左子树中的键值都 小于 此节点的键值. 任意节点的右子树中的键值都 大于 此节点的 ...

  4. LeetCode题解(1373):二叉搜索子树的最大键值和(Python)

    题目:原题链接(困难) 标签:树.二叉树.二叉搜索树 解法 时间复杂度 空间复杂度 执行用时 Ans 1 (Python) O ( N ) O(N) O(N) O ( N ) O(N) O(N) 47 ...

  5. LeetCode刷题复盘笔记—一文搞懂0 - 1背包之494. 目标和问题(动态规划系列第九篇)

    今日主要总结一下动态规划0-1背包的一道题目,494. 目标和问题 题目:494. 目标和 Leetcode题目地址 题目描述: 给你一个整数数组 nums 和一个整数 target . 向数组中的每 ...

  6. LeetCode刷题复盘笔记—一文搞懂完全背包之322. 零钱兑换问题(动态规划系列第十四篇)

    今日主要总结一下动态规划完全背包的一道题目,322. 零钱兑换 题目:322. 零钱兑换 Leetcode题目地址 题目描述: 给你一个整数数组 coins ,表示不同面额的硬币:以及一个整数 amo ...

  7. 二叉树中最大的二叉搜索子树

    问题描述 思路和代码 暴力方法是每个节点为根节点,然后判断以该节点为根节点的树是否是二叉搜索树,然后求规模最大的,这种方式的时间复杂度是O(N2)O(N^2)O(N2),空间复杂度是O(h)O(h)O ...

  8. leetcode刷题错误笔记(树之前)

    1.简单数据结构 1.1 数组一 二分查找: 看题目,数组升序,复杂度log n,想到二分查找. class Solution {public int search(int[] nums, int t ...

  9. 刷题记录8---验证二叉搜索树+二叉树的层序遍历+从前序与中序遍历序列构造二叉树+二叉树展开为链表+二叉树的最近公共祖先

    前言 所有题目均来自力扣题库中的hot 100,之所以要记录在这里,只是方便后续复习 98.验证二叉搜索树 题目: 给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树. 有效 二叉搜 ...

最新文章

  1. java五子棋代码详解_java打卡9.5 用方法封装循环点菜代码 详解
  2. Spring教程--IOC(注解方式)和整合junit
  3. 市面上主流的TCC框架介绍
  4. 《Kali Linux 渗透测试技术详解》笔记之 metasploit 学习纪要
  5. windows系统作为客户端时,linux中本地yum源挂载时,如何同时挂载DVD1和DVD2?
  6. 基于数据库的事务消息解决分布式事务方案
  7. C# 图片画矩形,添加文字
  8. 通过[AjaxMethod(AjaxPro.HttpSessionStateRequirement.ReadWrite)]html页面调用cs的方法
  9. “A class named TcxRect already exists”错误
  10. 金三银四大厂面经总结,mysql创建视图的sql语句
  11. android飞屏app下载地址,飞屏下载安卓最新版_手机app官方版免费安装下载_豌豆荚...
  12. jQuery复习:第五章
  13. lammps免费学习资料汇总
  14. WebEx Recorder:性能最好的录屏软件
  15. mysql中哪一个储存逻辑型_《VisualFoxPro》2018秋华东年季学期在线作业(一)二三...
  16. 第七章 变量进阶与点阵LED 练习题
  17. 读书感受 之 《三体》
  18. itunes未能连接到iphone软件更新服务器,iTunes无法联系iphone软件更新服务器 不可连接解决方法...
  19. Anytime Dynamic A* (AD*)算法分析
  20. 如何将png图片转为heif格式

热门文章

  1. 拼多多中秋美食一降到底|一度智信
  2. 0319 预习笔记和直播笔记
  3. 方程组的极大线性无关组与线性无关解的个数
  4. 好书推荐 |《6G需求与愿景》(文末赠书福利)
  5. xampp安装教程,以及mysql,apache的配置
  6. CIMAE 2017 农业博览会开幕 北京供销大数据集团助力智慧农业 4.0
  7. Office 办公——Word
  8. MOS认证考试——PPT2013(考试代码77-422)-张辛-专题视频课程
  9. 将微信、58、高德和大众点评一站拿下,“大热度”用统一后台让商家实现多渠道信息同步...
  10. Sleuth基本知识