算法题解记录25+++验证二叉搜索树(百日筑基)

题目描述:

        难度:中等

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

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

  • 节点的左

    子树

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

示例 1:

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

示例 2:

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

提示:

  • 树中节点数目范围在[1, 10^4] 内
  • -2^31 <= Node.val <= 2^31 - 1

解题准备:

        1.题意:题目要求验证一棵二叉树,是不是二叉搜索树(BST,即Binary Search Tree),那么我们首先要知道,一棵二叉搜索树是什么样的。

        简单地说,BST有如下性质:第一,其左子树上所有节点的值,都小于根节点值;第二,其右子树上所有节点的值,都大于根节点的值。第三,BST下所有节点,都有BST的性质。

        2.基本操作:就题目看,不涉及增删改,验证必然要涉及查找遍历,姑且认为只有查找和比较。

        3.基础原理:面对树的题目,我们应敏锐地察觉到至少两种方法---BFS广度优先搜索和DFS深度优先搜索。这两种方法几乎是树的算法的基础,大多数算法,都是在二者之上优化而得。

解题思路:

        朴素地说,对于新手,其实一看到这个题目,是不会有什么思路的。

        然而,如果说做过一些题,就能直接得到答案,也非常夸张。

        我在此分享我的错误思路。

        错误思路---左右递归判断

                我最开始的思路比较简单,基于两个基本原理:

                第一,如果一棵树左子节点left的值,大于或等于根节点root的值,说明它不是BST。

                第二,如果一棵树右子节点right的值,小于或等于根节点root的值,说明不是BST。

                这两个原理没有错,却不完全,如果一棵树满足这两个原理,也有可能不是BST,比如下图,7大于5,却满足这个原理,同样,3小于5,也满足这个原理。

                这两个原理没有满足整体,所以我编写的代码在运行之后也报错了,不过,我会在下面提供代码。我先在此说明我的思考过程。【如何将这两个原理转化为代码的】

                第一步,有了原理,尽量往dfs或者bfs方法上靠近。

                第二步,发现判断一个节点是否满足2个原理,只需要判断left与root的关系、right与root的关系即可。【异常处理还没做】

                第三步,得到问题:虽然判断一个节点很简单,怎么判断下一个呢?

                第四步,初始的朴素思想:干脆中序遍历或前序遍历一遍,得到所有节点(存储List),依次进行判断?

                第五步,想到优化思路,既然中序遍历能够遍历每个节点,为什么要遍历一边,然后又从List再遍历一遍?

                第六步,中序原理是左根右,访问操作只在根节点做,那么继续左中右方法,把判断左子放在“左”,判断右子放在“右”,根节点的数据访问忽略了,就可以了。

                第七步,异常:访问到空节点null。

                第八步:if限制,null节点直接返回,又因为要判断子节点与根节点的关系,返回明显不能处理,所以需要额外判断子节点是否为null。

                完成。

        正确思路---中序遍历序列

                这个思路首先要明白一件事:中序遍历,其顺序是左子树、根节点、右子树。

                其中,在左子树left中,其顺序也是left的左子树、left、left的右子树。

                如果一棵树只有一个节点,毫无疑问它是升序的。

                假设这棵树root,它的左子树是left,右子树是right。

                那么,中序遍历时,一定会访问到left的最左子节点X,它是升序的。

                接着,访问最左子节点的父节点F,由于BST性质,最左子X小于F,所以是升序的。

                然后,访问F的右子【也有可能是右子的左子、右子的左子的左子……】P,由于BST性质,XFP升序排列。

                由于这棵树升序排列,那么,它的父树也升序排列,最后,left升序排列,整个节点的中序序列,都升序排列。

                由此,我们只需要得到中序序列,即可判断。

        正确思路---区间逼近

                这个思路是题解的思路,不过与我的错误思路非常接近,它把我的2个原理推进了。

                原理1:左子节点left,一定比root小,左子的左子,一定比root小;左子的右子,一定比root小,并且比左子大……

                原理2:右子节点right,一定比root大,右子的右子,一定比root大;右子的左子,一定比root大,并且比右子小。

                如此,我们可以发现,我的错误思路,就在于判断时,只是把节点与其左右子进行判断,忽略了最重要的根节点。

                然而,问题出现了:如果我们持续用root的值,作为判断依据,如下图:5作为右边所有节点的下限,没有问题,但是9却比7要大,也就是说子树7不符合BST的性质,所以整棵树不是BST。

                换句话说,我们要求上下限是不断变化的,这其实也是二叉树的精髓部分。

                怎么做呢?

                一般的想法是,维护两个变量low、up,使它们代表某个节点的上下限,在每次判断时作为依据。

                不过,如果这两个变量只是代表1个节点上下限,那么我们要开辟一个非常大的空间,用来存储所有的变量对low、up。

                所以,借助递归的方案,我们把维护变量对的操作交给系统,我们只需要递归调用时,传递变量对即可。

解题难点分析:

        无

错误代码---我的思路:

class Solution {public boolean isValidBST(TreeNode root) {boolean flag = true;// 为null不判断if(root.left!=null){if(root.left.val>=root.val){return false;}flag = isValidBST(root.left);}// 看一下是否不是BSTif(!flag){return flag;}// 同理if(root.right!=null){if(root.right.val<=root.val){return false;}flag = isValidBST(root.right);}return flag;}
}

代码---中序遍历:

class Solution {public boolean isValidBST(TreeNode root) {// 存储节点List<Integer> data = new ArrayList<>();fuzhu(root, data);// 依次判断for(int i=0; i<data.size()-1; i++){if(data.get(i) >= data.get(i+1)){return false;}}return true;}private void fuzhu(TreeNode root, List<Integer> data){if(root==null){return;}fuzhu(root.left, data);data.add(root.val);fuzhu(root.right, data);}
}

代码---迭代上下限:

class Solution {public boolean isValidBST(TreeNode root) {return fuzhu(root, Long.MIN_VALUE, Long.MAX_VALUE);}private boolean fuzhu(TreeNode root, long low, long up){// 如果是空节点,即正确 if(root==null){return true;}// 如果超出界限,则错误// 我们可以看出来,每次递归判断,只能判断一个节点,我们不可能一起判断左子和右子if(root.val <= low || root.val >= up){return false;}// 返回左子节点、右子节点的判断。return fuzhu(root.left, low, root.val) && fuzhu(root.right, root.val, up);}
}

以上内容即我想分享的关于力扣热题25的一些知识。

        我是蚊子码农,如有补充,欢迎在评论区留言。个人也是初学者,知识体系可能没有那么完善,希望各位多多指正,谢谢大家。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

十二种网络威胁防护方案

一、SQL注入 SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严&#xff0c;攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句&#xff0c;在管理员不知情的情况下实现非法操作&#xff0c;以此来实现欺骗数据库服务器执行非授权的任…

基于网络的无人海洋船舶控制

书籍&#xff1a;Network-Based Control of Unmanned Marine Vehicles 作者&#xff1a;Yu-Long Wang&#xff0c;Qing-Long Han&#xff0c;Chen Peng&#xff0c;Lang Ma 出版&#xff1a;Springer 书籍下载-《基于网络的无人海洋船舶控制》控制系统中的通信网络可能引起延…

解决在Outlook中预定Teams会议不显示入会链接的问题

今天遇到一个很蛋疼的teams问题&#xff0c;花了点时间才解决。本来以为是很简单的问题&#xff0c;随便网上冲浪一下就能找到答案的&#xff0c;结果根本就没有好的解决方案&#xff0c;所以我分享出来希望后来的老哥少走点弯路。 问题描述 简单来说&#xff0c;就是在Outlo…

Star CCM+衍生零部件的创建

前言 在一个仿真计算项目中&#xff0c;分配零部件至区域、划分网格后。下一步可以先将需要监测的点、面建立出来&#xff0c;方便后续创建报告。Star中需要创建点、面是在衍生零部件下创建。衍生零部件→右键→新建&#xff08;如下图1所示&#xff09;。通过衍生零部件可以创…

15集合的应用

集合的概念 集合是一个容器&#xff0c;可以容纳其他类型的数据&#xff0c;前面所讲的数组就是一个集合。 所有的集合相关的类和接口都在java.util包下 特点 集合不能直接存储基本数据类型(但是代码上不需要体现&#xff0c;因为Java中有自动装箱)另外集合不能直接存储Java…

etcd集群恢复、单节点恢复操作手册

一、集群备份 备份方式&#xff1a;Jenkins触发每小时的定时任务&#xff0c;通过调取ansible的playbook进行etcd集群的数据备份和上传&#xff0c;默认只备份集群中的非leader成员&#xff0c;避免leader成员压力过大。将备份数据上传到对应的公有云对象存储&#xff0c;分别…

2024初会押题来了!经济法和实务,内部资料,赶快做一做!

初级会计考试考前冲刺策略 1. 核心知识点掌握 考前冲刺首先需要对核心知识点进行梳理和重点复习。《初级会计实务》和《经济法基础》是考试的两个主要科目&#xff0c;考生需要确保对其中的高频考点有深刻的理解。 2. 高效记忆技巧 对于容易遗忘的知识点&#xff0c;可以采用…

Bootloader+升级方案

随着设备的功能越来越强大&#xff0c;系统也越来越复杂&#xff0c;产品升级也成为了开发过程不可或缺的一道程序。在工程应用中&#xff0c;如何在不更改硬件的前提下通过软件的方式实现产品升级。通过Bootloader来实现固件的升级是一种极好的方式&#xff0c;Bootloader是单…

Springboot HelloWorld

新建一个maven工程 引入依赖项 <modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.11.RELEASE</version><…

ST表(静态RMQ问题)

static Range Max/Min Query ST表 利用的是动态规划的思想 状态&#xff1a; //st[i][j]-->区间长度为1<<j&#xff0c;在区间[i,i1<<j-1]上的最值 状态转移方程&#xff1a; st[i][j]max(st[i][j-1],st[i(1<<j-1)][j-1]);#include <iostream> #inc…

vue3延迟加载(异步组件​)defineAsyncComponent

最简单用法 Index.vue: <script setup> import { onMounted, defineAsyncComponent } from vue import ./index.cssconst Child defineAsyncComponent(() > import(./Child.vue))onMounted(() > {}) </script><template><div class"m-home-w…

C/C++:Windows中的静态链接库

静态链接库 静态链接库是在编译时将库的代码和数据复制到可执行文件中&#xff1b;这意味着最终的可执行文件包含了程序所需的所有函数和数据&#xff0c;因此它在运行时是完全独立的。 静态链接库工作原理&#xff1a; 开发人员创建一个或多个静态链接库&#xff0c;其中包含…