【算法题解】32. 验证二叉搜索树的递归解法

news2024/11/27 21:36:23

这是一道 中等难度 的题

https://leetcode.cn/problems/validate-binary-search-tree/

题目

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

输入:root = [2,1,3] 
输出:true 

示例 2:

输入:root = [5,1,4,null,null,3,6] 
输出:false 解释:根节点的值是 5 ,但是右子节点的值是 4 。 

提示:

  • 树中节点数目范围在 [ 1 , 1 0 4 ] [1, 10^4] [1,104]
  • − 2 31 < = N o d e . v a l < = 2 31 − 1 -2^{31} <= Node.val <= 2^{31} - 1 231<=Node.val<=2311

题解

根据有效搜索二叉树的定义,所有左子树和右子树自身必须也是二叉搜索树,然后子树的子树同样也需要满足是二叉搜索树,一层一层往下走且每一层的判断条件都一样,符合递归的的思路。

递归三要素:

  1. 递归函数:即根据定义判断是否满足条件:
  • 节点的左子树只包含 小于 当前节点的数。翻译成代码为 root.left.val < root.val
  • 节点的右子树只包含 大于 当前节点的数。翻译成代码为 root.right.val > root.val
  • 所有左子树和右子树自身必须也是二叉搜索树。翻译成代码为 isValidBST(root.left) && isValidBST(root.right)
  1. 边界条件:节点为空,直接返回true
  2. 还原现场:无需还原。

以Java代码为例:

class Solution {
    public boolean isValidBST(TreeNode root) {

        // 边界条件
        if(root == null){
            return true;
        }

        if(root.left != null && root.left.val >= root.val){
            return false;
        }

        if(root.right != null && root.right.val <= root.val){
            return false;
        }

        return isValidBST(root.left) && isValidBST(root.right);

    }
}

以上代码运行出错,以 [5,4,6,null,null,3,7] 为例:

右子树中的 3 小于 5,不满足定义的第二点:节点的右子树只包含 大于 当前节点的数。

所以在判断子树是否满足条件的时候不仅要和当前节点的值做比较,还要和当前节点的父节点的值做比较。

假如根节点的取值范围为 ( − ∞ , + ∞ ) (-∞, +∞) (,+):

  1. 左节点left的取值范围就应该是(-∞, root.value)
  • 下一层左节点取值范围:(-∞, left.value)
  • 下一层右节点取之范围:(left.value, root.value)
  1. 右节点right的取值范围就应该是(root.value, +∞)
  • 下一层左节点取值范围:(root.value, right.value)
  • 下一层右节点取之范围:(right.value, +∞)

这样就可以将每一个节点的值当作边界值一层一层的传下去,并用于约束子节点的取值范围。

Java 代码实现

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isValidBST(TreeNode root) {
        // 因为节点值可以取Integer.MIN_VALUE  和 Integer.MAX_VALUE,
        // 所以正负无穷的值必须 大于Integer.MAX_VALUE 和 小于Integer.MIN_VALUE

        // 根结点的取值范围在正负无穷之间
        return this.isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);

    }

    /**
    * @param minVal 最小值,不包含
    * @param maxVal 最大值,不包含
    */
    private boolean isValidBST(TreeNode node, long minVal, long maxVal) {
        // 边界条件
        if(node == null){
            return true;
        }

        // 判断节点值是否满足取值范围
        if(node.val >= maxVal || node.val <= minVal){
            return false;
        }

        // 左节点的值必须在父节点的取值范围内且需要小于父节点的值
        // 右节点的值必须在父节点的取值范围内且需要大于父节点的值
        return isValidBST(node.left, minVal, node.val) && isValidBST(node.right, node.val, maxVal);
    }
}

Go 代码实现

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func isValidBST(root *TreeNode) bool {
    return isValidBST2(root, math.MinInt64, math.MaxInt64)
}

func isValidBST2(root *TreeNode, minVal int, maxVal int) bool {
    if root == nil {
        return true
    }

    if root.Val >= maxVal || root.Val <= minVal {
        return false
    }

    return isValidBST2(root.Left, minVal, root.Val) && isValidBST2(root.Right, root.Val, maxVal)
    
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n), n n n 为节点个数,每个节点都要算一次,总共是 n n n 次。
  • 空间复杂度: O ( n ) O(n) O(n),空间复杂度为调用栈的深度,最多为 n n n 层,即二叉树退化成链表的时候。

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

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

相关文章

【瑞萨RA_FSP】DMAC/DTC编程实战

文章目录 一、DMAC存储器到存储器传输二、DTC外部中断触发传输 一、DMAC存储器到存储器传输 1. FSP配置 打开该工程的 FSP 配置界面。然后按如图步骤加入 DMAC。 加入 DMAC 后如下图所示。 单击上图中新添加的 r_dmac 框&#xff0c;然后在左下角的“属性”窗口配置 DMAC 模…

一分钟学一个 Linux 命令 - cd

前言 大家好&#xff0c;我是 god23bin。欢迎来到这个系列&#xff0c;每天只需一分钟&#xff0c;记住一个 Linux 命令不成问题。今天让我们从 cd 命令开始&#xff0c;掌握在 Linux 系统中切换目录的技巧。 什么是 cd 命令&#xff1f; cd 命令来自这么一个词语&#xff0…

liftOver 不同版本基因组文件相互转化

大家好&#xff0c;我是邓飞。前一段时间有小伙伴在星球提问&#xff1a;想将不同版本的SNP数据合并&#xff0c;不想重新call snp&#xff0c;想把绵羊的V2和V4版本的数据合并&#xff0c;具体来说&#xff0c;是V2转为V4然后与V4合并。 我建议用liftOver软件进行处理&#xf…

SAP-MM-采购折扣知识与会计处理

采购折扣&#xff0c;按折扣的方式和性质可以分为商业折扣和现金折扣两种方式。 商业折扣是指购货方批量进货时&#xff0c;要求销货方按零售价格打一个折扣而少支付的货款金额。企业之间的商品购销活动中&#xff0c;商业折扣是一种较常见的交易现象。 现金折扣则是指在赊购条…

数列DP进阶

目录 一&#xff0c;斜率优化 1&#xff0c;斜率优化原理 2&#xff0c;凸包和斜率计算 3&#xff0c;实战 黑暗爆炸 - 4709 柠檬 二&#xff0c;else 力扣 644. 子数组最大平均数 II&#xff08;最大子段和二分&#xff09; ​力扣 646. 最长数对链 力扣 1235. 规划兼…

浅科普一下计算机发展史阶段及那些不为人知的重要里程碑

目录 〇、前言 一、计算机发展历史阶段 二、计算机发展史中重要的里程碑 三、计算机对人类社会发展的重要性 四、计算机的应用领域 五、常见计算机辅助技术 六、总结 〇、前言 计算机的诞生无疑对人类社会的发展起着至关重要的巨大作用。计算机发明者名叫约翰冯诺依曼&a…

SAP-MM-发票-采购运费

采购运费是采购业务中一种特殊的定价&#xff0c;在SAP系统中&#xff0c;交货成本和其相近&#xff0c;是指在货物交付过程中发生的运输成本&#xff0c;只要有货物交付&#xff0c;就会有运费&#xff0c;而运费或者由采购方承担&#xff0c;或者由销售方承担&#xff0c;国内…

03SpringCloud Docker

Docker (1&#xff09;从VM与Docker框架中&#xff0c;直观上VM多了一层Guest OS&#xff0c;同时Hypervisor会对硬件资源进行虚拟化&#xff0c;docker直接使用硬件资源&#xff0c;所以资源利用率相对docker低。 &#xff08;2&#xff09;openstack能够以10台/min的速度创建…

SSM框架学习-拦截器

1. 简介 在Spring框架中&#xff0c;拦截器是一种很重要的组件&#xff0c;它们允许在请求到达控制器之前或之后执行一些代码。拦截器在请求处理的特定点进行拦截&#xff0c;然后通过逻辑来决定是否将控制器的处理传递给下一个处理程序。 在Spring中&#xff0c;拦截器是由实现…

【MATLAB速成】知识点总结(通俗易懂,学不会来打我)

【MATLAB速成】知识点总结&#xff08;通俗易懂&#xff0c;学不会来打我&#xff09; 一、概念 MATLAB的中文名称是&#xff08;矩阵实验室&#xff09;&#xff0c;英文全称是&#xff08;Matrix Laboratory&#xff09;&#xff0c;是一种以&#xff08;矩阵计算&#xff…

【学习日记2023.5.30】之 管理端订单模块完善_调用百度地图优化用户端提交订单是否超出配送距离

文章目录 9. 管理端订单模块完善9.1 需求分析和涉及9.2 代码开发Controller层Service层接口Service层实现类Mapper层 9.3 功能测试9.4 提交代码9.5 优化用户下单功能&#xff0c;引入距离判断 9. 管理端订单模块完善 订单搜索各个状态的订单数量统计查询订单详情接单拒单取消订…

古诗生成-pytorch

本文为RNN做古诗生成的一个小demo&#xff0c;只要是为了完成课上的作业&#xff08;由于训练比较慢&#xff0c;所以周期仅设置为3&#xff0c;大一点性能可能会更好&#xff09;&#xff0c;如有需要可以在这基础之上进行加工&#xff0c;数据集没办法上传&#xff0c;如有需…

FreeRTOS_从底层学习实时操作系统

目录 1. 裸机系统和多任务系统 2. 任务的定任务切换的实现 2.1 什么是任务&#xff1f; 2.2 调度器 2.3 临界段 3. 空闲任务和阻塞延迟 4. 时间片 1. 裸机系统和多任务系统 裸机系统&#xff1a; 裸机系统分为轮询系统和前后台系统&#xff1b;&#xff08;51单片机就属…

八大排序:直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序、计数排序

文章目录 排序概念常见的排序算法常见排序算法的实现直接插入排序希尔排序选择排序堆排序冒泡排序快速排序递归实现Hoare版本挖坑法前后指针法 非递归实现Hoare版本挖坑法前后指针法 快速排序俩个优化 归并排序递归实现非递归实现外排序 计数排序 常见排序算法的性能分析 排序概…

【已完美解决】scons问题求助:如何设置编译输出目录搞清楚后,有些编译输出的obj文件却在源码目录,而不是设置的输出目录。

【已完美解决】scons问题求助&#xff1a;如何设置编译输出目录搞清楚后&#xff0c;有些编译输出的obj文件却在源码目录&#xff0c;而不是设置的输出目录。 文章目录 1 前置背景2 我的疑问3 一手点拨4 问题解决 1 前置背景 最近在基于目前已有的rt-thread构建框架&#xff0…

【Spring源码解读一】IoC容器之AnnotationConfigApplicationContext

根据AnnotationConfigApplicationContext类去阅读其将Bean对象交给IoC容器管理的过程。以下这三个代码块是将配置类注册进IoC容器的例子。下面是关于这个类的继承与实现的类图关系树。 public class Test {public static void main(String[] args) {// 配置类注册进IoC容器App…

解决Ubuntu16中安装opencv后找不到vtk库的问题

最近一个项目中要用到OpenCV的VTK库&#xff0c;但引入头文件#include <opencv2/viz.hpp>时却说找不到这个库&#xff0c;网上搜了下说在编译opencv源码的时候&#xff0c;需要加上编译VTK库的选项&#xff0c;于是重新下载、编译、安装了源码&#xff0c;在cmake时加上了…

最流行的AI绘图工具Midjourney,你不得不知道的使用技巧

​关注文章下方公众号&#xff0c;可免费获取AIGC最新学习资料 本文字数&#xff1a;1500&#xff0c;阅读时长大约&#xff1a;10分钟 Midjourney成为了最受欢迎的生成式AI工具之一。它的使用很简单。输入一些文本&#xff0c;Midjourney背后的大脑&#xff08;或计算机&#…

Linux 权限

目录 一、 从问题开始 问题一: 什么叫shell? 问题二: 为什么不能直接使用kernel呢? 问题三: shell 与bash 有什么不同吗? 二、 Linux权限 0x01 Linux用户 0x02 切换用户命令 0x03 sudo命令 0x04 权限的相关概念 0x05 chmod 0x06 chown 0x07 chgrp 0x08 文件权…

重磅!软著申请不需要邮寄纸质材料啦,附软著申请流程。

重磅&#xff01;软著申请不需要邮寄纸质材料啦&#xff0c;附软著申请流程。 最新消息申请流程一&#xff0c;准备申请材料二&#xff0c;申请人填写申请表三&#xff0c;提交申请材料四&#xff0c;补正五&#xff0c;审查六&#xff0c;发布公告七&#xff0c;接受异议八&am…