【leetcode】1373. 二叉搜索子树的最大键值和

news/2024/12/21 20:36:22/

二叉搜索子树的最大键值和

  • 问题描述
  • 问题简单分析
  • 提交之旅
    • 第一次提交-失败
    • 第二次提交-失败
    • 第三次提交-成功

问题描述

二叉搜索子树的最大键值和
给你一棵以 root 为根的 二叉树 ,请你返回 任意 二叉搜索子树的最大键值和。
二叉搜索树的定义如下:

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

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

问题简单分析

对于树的处理一般都采用递归的方式,再配以某种遍历(前序、中序、后序)方式。对于该问题也是类似的处理方式。
本问题本质上从一颗二叉树中查找节点之和最大二叉搜索树

判断某个节点的子树是否是二叉搜索树,需要先检查该节点的左右子树是否是二叉搜索树,再判断该节点是否是二叉搜索树,因此这里使用的是后序遍历的方式。当左子树或右子树任意一个不满足时,该子树的祖先节点都将不是二叉搜索树。

父节点需要能够区分左右子树是否是二叉搜索树。

基于以上考虑,就有了第一次提交。

提交之旅

第一次提交-失败

二叉搜索子树的区分:当返回值小于0,表示不是二叉搜索树

class Solution {
public:int maxSumBST(TreeNode* root) {int maxSum = 0;maxSumBST(root, maxSum);return maxSum;}int maxSumBST(TreeNode* root, int& maxSum) {//如果是空节点,则直接返回0,避免左右子树这里还要加个是否为空的处理逻辑if(root == nullptr){return 0;}int leftSum = maxSumBST(root->left, maxSum);int rightSum = maxSumBST(root->right, maxSum);//后序处理逻辑//当左右子树任意个小于0,则返回-1,表示不是二叉搜索树if(leftSum < 0 || rightSum < 0){return -1;}//左节点不小于父节点,则说明不是二叉搜索树,返回-1 ---- 这里存在问题if(root->left != nullptr && root->val <= root->left->val){return -1;}//右节点不大于父节点,则说明不是二叉搜索树,返回-1 ---- 这里存在问题if(root->right != nullptr && root->val >= root->right->val){return -1;}//统计二叉搜索树的节点之和int sum = root->val + leftSum + rightSum;if(maxSum < sum){maxSum = sum;}return sum;}};

提交后就失败了,失败用例:[1,null,10,-5,20],期望是25,而实际返回20。原因如下:
节点10的左子节点是一个叶子节点,但是由于其值为-5(小于0),被误判为非二叉搜索树,导致[10,-5,20] 这颗子树也被误判为非二叉搜索树,因此最终返回20。

因此必须想其他方案来区分是否是二叉搜索树,因此就有了第二次提交。

第二次提交-失败

二叉搜索子树的区分:通过tuple(是否是二叉搜索树,树节点之和,最小节点)中专门字段来区分,当为true,则表示是二叉搜索树

class Solution {
public:int maxSumBST(TreeNode* root) {int maxSum = 0;maxSumBST(root, maxSum);return maxSum;}tuple<bool,int,TreeNode*> maxSumBST(TreeNode* root, int& maxSum) {if(root == nullptr){return make_tuple(true, 0, nullptr);}tuple<bool,int,TreeNode*> left = maxSumBST(root->left, maxSum);tuple<bool,int,TreeNode*> right = maxSumBST(root->right, maxSum);if(!std::get<0>(left) || !std::get<0>(right)){return make_tuple(false, 0, nullptr);}//左子树的最小节点不小于父节点,则说明不是二叉搜索树,返回-1 ---- 这里存在问题if(std::get<2>(left) && root->val <= std::get<2>(left)->val) {return make_tuple(false, 0, nullptr);}//右子树的最小节点不大于父节点,则说明不是二叉搜索树,返回-1if(std::get<2>(right) && root->val >= std::get<2>(right)->val) {return make_tuple(false, 0, nullptr);}int sum = root->val + std::get<1>(left) + std::get<1>(right);if(maxSum < sum){maxSum = sum;}return make_tuple(true, sum, (root-> left ? root->left : root));}};

为啥tuple中会有 最小节点?这是因为用例[1,null,10,-5,20]期望是25,而实际返回26,这是因为原来的实现是通过判断节点跟其左右节点的关系来判断是否是二叉搜索树,这就导致误将[1,null,10,-5,20]也当作合法的二叉搜索树。
[10,-5,20]是合法的二叉搜索树,[1,null,10]也是合法的二叉搜索树,但是[1,null,10,-5,20]树中-5在1的右子树上,但小于1,所以这棵树不是合法的二叉搜索树。

既然右子树的最小节点不大于父节点,那么应该是左子树的最大节点不小于父节点,而不是左子树的最小节点不小于父节点?这是因为测试时不小心点了提交!

第三次提交-成功

二叉搜索树判定:父节点需要大于左子树的最大节点,小于右子树的最小节点。

class Solution {
public:int maxSumBST(TreeNode* root) {int maxSum = 0;maxSumBST(root, maxSum);return maxSum;}//返回:是否是二叉搜索树,树节点之和,最小节点,最大节点tuple<bool,int,TreeNode*,TreeNode*> maxSumBST(TreeNode* root, int& maxSum) {if(root == nullptr){return make_tuple(true, 0, nullptr, nullptr);}tuple<bool,int,TreeNode*,TreeNode*> left = maxSumBST(root->left, maxSum);tuple<bool,int,TreeNode*,TreeNode*> right = maxSumBST(root->right, maxSum);if(!std::get<0>(left) || !std::get<0>(right)){return make_tuple(false, 0, nullptr, nullptr);}//根节点需要大于左子树的最大值if(std::get<3>(left) && root->val <= std::get<3>(left)->val) {return make_tuple(false, 0, nullptr, nullptr);}//根节点需要小于右子树的最小值if(std::get<2>(right) && root->val >= std::get<2>(right)->val) {return make_tuple(false, 0, nullptr, nullptr);}int sum = root->val + std::get<1>(left) + std::get<1>(right);if(maxSum < sum){maxSum = sum;}//找到该节点子树的最大最小节点TreeNode* min = std::get<2>(left) ? std::get<2>(left) : root;TreeNode* max = std::get<3>(right) ? std::get<3>(right) : root;;return make_tuple(true, sum, min, max);}};

核心关键点:

  • 后序遍历
  • 二叉搜索树的判定:节点值大于左子树的最大值,小于右子树的最小值。

http://www.ppmy.cn/news/75316.html

相关文章

接口测试的流程?怎么设计接口测试用例?两张图给你讲的明明白白

目录 一、简介 二、接口测试的流程 三、为什么要写用例 四、接口用例设计 一、简介 在开始接口测试之前&#xff0c;我们想一下&#xff0c;接口测试的流程是什么&#xff1f;说到这里&#xff0c;有些人就会产生好奇和疑问&#xff0c;心里mmp&#xff1a;接口测试要什么流…

Android Qcom USB Driver学习(十一)

该系列文章总目录链接与各部分简介&#xff1a; Android Qcom USB Driver学习(零) 基于TI的Firmware Update固件升级的流程分析usb appliction layers的数据 USB Protocol Package ①/② map to check password correct Package Format: Byte[0] Report Id Byte[1] Valid L…

阿里三面过了,却无理由挂了,HR反问一句话:为什么不考虑阿里?

进入互联网大厂一般都是“过五关斩六将”&#xff0c;难度堪比西天取经&#xff0c;但当你真正面对这些大厂的面试时&#xff0c;有时候又会被其中的神操作弄的很是蒙圈。 近日&#xff0c;某位测试员发帖称&#xff0c;自己去阿里面试&#xff0c;三面都过了&#xff0c;却被…

IEEE独立出版 | 第七届计算机科学与智能控制国际会议(ISCSIC 2023)

会议简介 Brief Introduction 第七届计算机科学与智能控制国际会议(ISCSIC 2023) 会议时间&#xff1a;2023年10月27日-29日 召开地点&#xff1a;中国南京 大会官网&#xff1a; ISCSIC 2023-2023 7th International Symposium on Computer Science and Intelligent Control(I…

20230520查找中国移动的APP在RK3566下调用UVC摄像头出错

20230520查找中国移动的APP在RK3566下调用UVC摄像头出错 2023/5/20 23:34 SDK&#xff1a;Android12RK3566平台 android12 UVC camera 没插摄像头&#xff0c;但是/dev/video0-13标号被占用&#xff0c;是啥原因导致的 板子上也没有摄像头 【板子没有接CSI/MIPI接口的I2C通道…

如何快速搭建springboot项目

在IntelliJ IDEA中&#xff0c;可以按照以下步骤快速创建一个Spring Boot项目&#xff1a; 1. 打开 IntelliJ IDEA&#xff0c;点击欢迎界面上的"Create New Project"或者从菜单栏选择"File" -> "New" -> "Project"。 2. 在创…

C++ CS留学生期末答疑2

#include <iostream>using namespace std;int main() {int i 0;while (i < 10) {if (i % 2 0) {continue;}printf("%d", i);i i 1;}return 0; }#include <iostream>这是一个预处理指令&#xff0c;用于包含输入输出流库&#xff0c;使我们可以使用…

shell——免交互

一、Here Document 免交互 概述 常用的交互程序&#xff1a;read&#xff0c;ftp&#xff0c;passwd&#xff0c;su&#xff0c;sudo。 cat也可配合免交互的方式重定向输出到文件。 作用&#xff1a; 使用I/O重定向的方式将命令列表提供给交互式程序&#xff1b;标准输入的…