二叉搜索树题目:验证二叉搜索树

news2024/11/14 3:34:31

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
  • 解法一
    • 思路和算法
    • 代码
    • 复杂度分析
  • 解法二
    • 思路和算法
    • 代码
    • 复杂度分析
  • 解法三
    • 思路和算法
    • 代码
    • 复杂度分析

题目

标题和出处

标题:验证二叉搜索树

出处:98. 验证二叉搜索树

难度

3 级

题目描述

要求

给定一个二叉树的根结点 root \texttt{root} root,判断其是否是一个有效的二叉搜索树。

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

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

示例

示例 1:

示例 1

输入: root   =   [2,1,3] \texttt{root = [2,1,3]} root = [2,1,3]
输出: true \texttt{true} true

示例 2:

示例 2

输入: root   =   [5,1,4,null,null,3,6] \texttt{root = [5,1,4,null,null,3,6]} root = [5,1,4,null,null,3,6]
输出: false \texttt{false} false
解释:根结点的值是 5 \texttt{5} 5,但是右子结点的值是 4 \texttt{4} 4

数据范围

  • 树中结点数目在范围 [1,   10 4 ] \texttt{[1, 10}^\texttt{4}\texttt{]} [1, 104]
  • -2 31 ≤ Node.val ≤ 2 31 − 1 \texttt{-2}^\texttt{31} \le \texttt{Node.val} \le \texttt{2}^\texttt{31} - \texttt{1} -231Node.val2311

解法一

思路和算法

对于二叉搜索树中的任意结点,其左子树中的结点值都小于当前结点值,其右子树中的结点值都大于当前结点值,因此二叉搜索树的每一个子树(包括原二叉搜索树本身)都有结点值的取值范围。对于原二叉搜索树,结点值的取值范围是 ( − ∞ , + ∞ ) (-\infty, +\infty) (,+),如果根结点值为 r r r,则根结点的左子树和右子树的结点值取值范围分别是 ( − ∞ , r − 1 ] (-\infty, r - 1] (,r1] [ r + 1 , + ∞ ) [r + 1, +\infty) [r+1,+)

由于数据范围限制了二叉搜索树中的结点值的取值范围,因此上述取值范围可以用闭区间表示。对于二叉搜索树的每一个子树(包括原二叉搜索树本身),如果其结点值的取值范围是 [ lowerBound , upperBound ] [\textit{lowerBound}, \textit{upperBound}] [lowerBound,upperBound],子树的根结点值为 mid \textit{mid} mid,则该子树的左子树和右子树的结点值取值范围分别是 [ lowerBound , mid − 1 ] [\textit{lowerBound}, \textit{mid} - 1] [lowerBound,mid1] [ mid + 1 , upperBound ] [\textit{mid} + 1, \textit{upperBound}] [mid+1,upperBound]

判断给定的二叉树是否是二叉搜索树,可以通过判断给定的二叉树的每一个子树是否是二叉搜索树,即每一个子树是否都满足结点值在特定取值范围内。从根结点开始遍历二叉树,对于每个结点,判断其子树的结点值是否在特定取值范围内。

上述过程是一个递归的过程。递归的终止条件有两种情况。

  • 当前结点为空,空树是二叉搜索树。

  • 当前结点不为空且当前结点值不在特定取值范围内,二叉树不是二叉搜索树。

对于其余情况,根据当前子树的取值范围和当前结点值确定左子树和右子树的取值范围,对左子树和右子树调用递归。

由于二叉树中的结点值取值范围是 [ − 2 31 , 2 31 − 1 ] [-2^{31}, 2^{31} - 1] [231,2311],为了避免溢出,取值范围的上界和下界应使用数据类型 long \texttt{long} long,用 [ − 2 63 , 2 63 − 1 ] [-2^{63}, 2^{63} - 1] [263,2631] 作为原二叉树的取值范围。

代码

class Solution {
    public boolean isValidBST(TreeNode root) {
        return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
    }

    public boolean isValidBST(TreeNode node, long lowerBound, long upperBound) {
        if (node == null) {
            return true;
        }
        if (node.val < lowerBound || node.val > upperBound) {
            return false;
        }
        return isValidBST(node.left, lowerBound, (long) node.val - 1) && isValidBST(node.right, (long) node.val + 1, upperBound);
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。每个结点都被访问一次。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是递归调用的栈空间,取决于二叉树的高度,最坏情况下二叉树的高度是 O ( n ) O(n) O(n)

解法二

思路和算法

二叉树的中序遍历的方法为:依次遍历左子树、根结点和右子树,对于左子树和右子树使用同样的方法遍历。根据二叉搜索树的性质可知,对于值为 x x x 的结点,值小于 x x x 的结点一定在值等于 x x x 的结点之前被访问,值大于 x x x 的结点一定在值等于 x x x 的结点之后被访问,因此二叉搜索树的中序遍历序列一定是递增的。只要得到二叉树的中序遍历序列,即可根据中序遍历序列是否递增判断二叉树是否是二叉搜索树。

由于只是判断二叉树的中序遍历序列是否递增,因此不需要存储完整的中序遍历序列,而是只需要存储上一个遍历到的结点值。每次访问结点时,比较当前结点值和上一个结点值。如果存在一个结点值小于等于上一个结点值,则中序遍历序列不是递增的,二叉树不是二叉搜索树。如果每个结点值都大于上一个结点值,则中序遍历序列是递增的,二叉树是二叉搜索树。

由于二叉树中的结点值取值范围是 [ − 2 31 , 2 31 − 1 ] [-2^{31}, 2^{31} - 1] [231,2311],因此将上一个结点值初始化为 − 2 63 -2^{63} 263。每次访问结点之后,需要将上一个结点值更新为当前结点值。

代码

class Solution {
    public boolean isValidBST(TreeNode root) {
        Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
        long prev = Long.MIN_VALUE;
        TreeNode node = root;
        while (!stack.isEmpty() || node != null) {
            while (node != null) {
                stack.push(node);
                node = node.left;
            }
            node = stack.pop();
            if (node.val <= prev) {
                return false;
            }
            prev = node.val;
            node = node.right;
        }
        return true;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。每个结点都被访问一次。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是栈空间,取决于二叉树的高度,最坏情况下二叉树的高度是 O ( n ) O(n) O(n)

解法三

思路和算法

解法二虽然不需要存储中序遍历序列,但是仍需要使用栈空间。使用莫里斯遍历可以将空间复杂度降低到 O ( 1 ) O(1) O(1)

遍历过程中维护上一个结点值。每次访问结点时,比较当前结点值和上一个结点值。如果存在一个结点值小于等于上一个结点值,则中序遍历序列不是递增的,二叉树不是二叉搜索树。如果每个结点值都大于上一个结点值,则中序遍历序列是递增的,二叉树是二叉搜索树。

代码

class Solution {
    public boolean isValidBST(TreeNode root) {
        long prev = Long.MIN_VALUE;
        TreeNode node = root;
        while (node != null) {
            if (node.left == null) {
                if (node.val <= prev) {
                    return false;
                }
                prev = node.val;
                node = node.right;
            } else {
                TreeNode predecessor = node.left;
                while (predecessor.right != null && predecessor.right != node) {
                    predecessor = predecessor.right;
                }
                if (predecessor.right == null) {
                    predecessor.right = node;
                    node = node.left;
                } else {
                    predecessor.right = null;
                    if (node.val <= prev) {
                        return false;
                    }
                    prev = node.val;
                    node = node.right;
                }
            }
        }
        return true;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。使用莫里斯遍历,每个结点最多被访问两次。

  • 空间复杂度: O ( 1 ) O(1) O(1)

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

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

相关文章

K8S之Pod常见的状态和重启策略

Pod常见的状态和重启策略 常见的Pod状态PendingPodScheduledUnschedulablePodInitializingImagePullBackOffInitializedRunningErrorCrashLoopBackOffTerminatingSucceededFailedEvictedUnknown Pod的重启策略使用Always重启策略使用Never重启策略使用OnFailure重启策略(常用) …

16、prometheus + grafana + alertmanager

16、prometheus grafana alertmanager k8s 手撕方式安装 prometheus grafana alertmanager k8s版本&#xff1a;k8s-1.29.1 prometheus grafana alertmanager 监控报警 1、k8s 手撕方式安装 prometheus mkdir ~/prometheus-ymlkubectl create ns monitoringcat > ~/…

python创建udf函数步骤

一、目标 实现一个函数&#xff0c;传入两个datetime类型的参数&#xff0c;返回double类型的工作日天数 二、思路 如何计算差值&#xff1f; 如果开始时间和结束时间在同一天&#xff1a;实现同 datediff(end, start, ‘ss’) / 86400.0 如果开始时间和结束时间在不同天&am…

c语言--一维数组传参的本质(详解)

目录 一、前言二、代码三、形式3.1形式13.2形式2 四、总结 一、前言 首先从⼀个问题开始&#xff0c;我们之前都是在函数外部计算数组的元素个数&#xff0c;那我们可以把函数传给⼀个函数后&#xff0c;函数内部求数组的元素个数吗&#xff1f; 二、代码 直接上代码&#x…

代码随想录算法训练营第十五天|102.二叉树的层序遍历、226.翻转二叉树

102.二叉树的层序遍历 刷题https://leetcode.cn/problems/binary-tree-level-order-traversal/description/文章讲解https://programmercarl.com/0102.%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%B1%82%E5%BA%8F%E9%81%8D%E5%8E%86.html视频讲解https://www.bilibili.com/video…

Mountain Lake - Forest Pack

从头开始构建的50个岩石森林资源集合,充分利用了HDRP。还支持Universal 和Built-In。 支持Unity 2020.3+、高清渲染管线、通用渲染管线、标准渲染管线。导入包后,按照README中的说明进行操作。 Mountain Lake - Rock & Tree Pack是一个由50个准备好的资源组成的集合,从头…

Python datetime 模块的高级应用

Python datetime 模块的高级应用 介绍方法时区处理日期格式化日期计算常见问题及解决方案代码日历应用时间序列分析 介绍 datetime 模块是 Python 中用于处理日期和时间的标准库模块。它提供了日期和时间类型&#xff08;date、time、datetime&#xff09;以及与日期和时间相关…

创建个人的AI智能体agent

我在魔塔空间创建了一个作息时间管理的agent >>>>>点我体验<<<<<< Agent并非ChatGPT升级版&#xff0c;它不仅告诉你“如何做”&#xff0c;更会帮你去做&#xff01; 尽管ChatGPT的知识储备接近“全知全能”&#xff0c;但当你试着以AI助手…

K8S之运用节点选择器指定Pod运行的节点

node节点选择器的使用 使用场景实践使用nodeName使用nodeSelectornodeName和nodeSelector混合使用1、设置了nodeName 和 设置 Node上都不存在的标签。看调度情况2、设置nodeName 为node1 和 设置 node2上才有的标签。看调度情况 实践总结 使用场景 默认情况&#xff0c;在创建…

系统架构21 - 统一建模语言UML(下)

UML图 UML中的图分类作用 视图用例视图逻辑视图进程视图实现视图部署视图 UML中的图 “图”是一组元素的图形表示&#xff0c;大多数情况下把图画成顶点&#xff08;代表事物&#xff09;和弧&#xff08;代表关系&#xff09;的连通图。为了对系统进行可视化&#xff0c;可以…

浏览器F12调试

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目…

静态时序分析:静态时序分析的原理及其两种模式PBA、GBA

相关阅读 静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html?spm1001.2014.3001.5482 静态时序分析有两种模式&#xff1a;PBA(Path Based Analysis)和GBA(Graph Based Analysis)&#xff0c;PBA是基于路径的分析模式而GBA则是基于图的分析模式。在…

【C++修行之道】(引用、函数提高)

目录 一、引用 1.1引用的基本使用 1.2 引用注意事项 1.3 引用做函数参数 1.4 引用做函数返回值 1.5 引用的本质 1.6 常量引用 1.7引用和指针的区别 二、函数提高 2.1 函数默认参数 2.2函数占位参数 2.3 函数重载 2.4函数重载注意事项 一、引用 1.1引用的基本使用 …

如何将mongodb+django部署到云服务器上(备份)

在有了一台云服务器之后&#xff0c;我们就可以把写在本机上的程序&#xff0c;搬到服务器上了。采用WinSCP在本机和服务器之间交换文件&#xff1b;FinalShell来操作服务器。 1、mongodb-本机到服务器 主要是三个步骤&#xff1a;dump本地数据库-上传-导入&#xff0c;详情请…

基于大规模连续多目标优化的共轭梯度-进化集成算法

声明&#xff1a;文章题目字数有限&#xff0c;翻译水平有限&#xff0c;仅供参考&#xff01; 原题目: Integrating Conjugate Gradients Into Evolutionary Algorithms for Large-Scale Continuous Multi-Objective Optimization 引&#xff1a;这么久了&#xff0c;又捡起…

Pandas 对带有 Multi-column(多列名称) 的数据排序并写入 Excel 中

Pandas 从Excel 中读取带有 Multi-column的数据 正文 正文 我们使用如下方式写入数据&#xff1a; import pandas as pd import numpy as npdf pd.DataFrame(np.array([[10, 2, 0], [6, 1, 3], [8, 10, 7], [1, 3, 7]]), columns[[Number, Name, Name, ], [col 1, col 2, co…

springboot165科研工作量管理系统的设计与实现

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

第4章——深度学习入门(鱼书)

第4章 神经网络的学习 本章的主题是神经网络的学习。这里所说的“学习”是指从训练数据中自动获取最优权重参数的过程。本章中&#xff0c;为了使神经网络能进行学习&#xff0c;将导入损失函数这一指标。而学习的目的就是以该损失函数为基准&#xff0c;找出能使它的值达到最…

逐鹿比特币生态,Elastos 携新作 BeL2「重出江湖」

撰文&#xff1a;Babywhale&#xff0c;Techub News 文章来源Techub News&#xff0c;搜Tehub News下载查看更多Web3资讯。 刚刚过去的 2023 年&#xff0c;「比特币生态」成为了市场的绝对焦点之一。从铭文开始&#xff0c;到重新走进大众视野的 Stacks 与比特币闪电网络&am…

LeetCode Python - 3.无重复字符的最长子串

文章目录 题目答案运行结果 题目 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”&#xff0c;所以其长度为 3。 示例 2: 输入: s “bbbbb” 输出: 1 解释:…