「这是我参与2022首次更文挑战的第24天,活动详情查看:2022首次更文挑战」
前言
为什么我们要使用二叉搜索树呢?
回答这个问题我们先说一下二叉树的由来
在链表中,插入、删除速度很快,但查找速度较慢。
在数组中,查找速度很快,但插入删除速度很慢。
为了解决这个问题,找寻一种能够在插入、删除、查找、遍历等操作都相对快的容器,于是人们发明了二叉树。
值得注意的是,无特征的二叉树在工业上是没啥用处的,一般都是用的bst、avl等具有特殊特征的二叉树。
比如在bst中,中序遍历可以得到顺序输出,插入查找删除的速度都相当快速(logn).
这边说的bst,就是二叉搜索树,既然他这么快速,那我们肯定要学呀
那么我们首先要先验证是不是一个搜索二叉树,来吧,真题来了
题目
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
- 节点的左子树只包含 小于 当前节点的数。
- 节点的右子树只包含 大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:root = [2,1,3] 输出:true
分析
第一步:从题目中提取关键字
- 节点的左子树只包含 小于 当前节点的数。
- 节点的右子树只包含 大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
也就是说左<根<右,并且左子树和右子树也都要满足左<根<右
这道题其实主要从二叉搜索树的定义来去做
我们先来回顾一下二叉搜索树的定义
- 它可以是一棵空树
- 它可以是一棵由根结点、左子树、右子树组成的树,同时左子树和右子树都是二叉搜索树,且左子树上所有结点的数据域都小于等于根结点的数据域,右子树上所有结点的数据域都大于等于根结点的数据域
只有符合以上两种情况之一的二叉树,可以称之为二叉搜索树。
所以我们要检验对非空的树中的左右子树进行遍历,看是否满足左<根<右
我们先初始化遍历函数,并设置初始值为极小和极大
var isValidBST = function(root) {
const dfs = function(minValue,root,maxValue){
}
// 遍历是否满足左<根<右
// 为什么我们不直接dfs(root.left,root,root.right),因为要判断的太多,而极小和极大是绝对满足,所以我们初始化用极小和极大,然后在不断变化左子树和右子树
return dfs(-Infinity,root,Infinity)
};
遍历的时候就把判断是否是二叉树的条件写上,写上什么时候是true,什么时候是false
那么答案也就出来了
题解
var isValidBST = function(root) {
const dfs=function(minValue,root,maxValue){
if(!root){
return true;
}
if(root.val<=minValue||root.val>=maxValue){
return false;
}
return dfs(minValue,root.left,root.val)&&dfs(root.val,root.right,maxValue);
}
// 遍历是否满足左<中<右
return dfs(-Infinity,root,Infinity)
};
但是我觉得吧,面试的时候这种方法并不好写出来,因为这种方法并不好和我们已知的一些二叉树遍历方法联系起来,所以我们要往已知的方法上想
搜索二叉树的特点是什么?
中序遍历后,从前到后的数字都是有序的,这句话真实醍醐灌顶呀,我们只需要一次中序遍历,然后每次比对当前的值是否大于前面的值,如果遍历结束后,都满足的话,那就是搜索二叉树
首先我们先在函数中在写一个遍历函数
var isValidBST = function(root) {
const travers = function(root){
// 判断空值二叉搜索树
// 如果是空树的,也是二叉搜索树,返回true
if(!root){
return true;
}
// 判断有值二叉搜索树
travers(root.left)
console.log(root.val);
travers(root.right)
}
travers(root);
};
然后初始化pre也就是前一个数字,和flag也就是要返回的结果
var isValidBST = function(root) {
// 初始化前一个数为最小值
let pre = -Infinity;
let flag = true;
const travers = function(root){
// 判断空值二叉搜索树
// 如果是空树的,也是二叉搜索树,返回true
if(!root){
return true;
}
// 判断有值二叉搜索树
travers(root.left)
console.log(root.val);
travers(root.right)
}
travers(root);
return flag;
};
如果pre大于等于当前值得话,那么flag为false; 然后我们在每次打印的时候改变pre的值,那么答案也就出来了
题解
var isValidBST = function(root) {
// 初始化前一个数为最小值
let pre = -Infinity;
let flag = true;
const travers = function(root){
// 判断空值二叉搜索树
// 如果是空树的,也是二叉搜索树,返回true
if(!root){
return true;
}
// 判断有值二叉搜索树
travers(root.left)
if(pre>=root.val){
flag = false;
}
pre = root.val;
travers(root.right)
}
travers(root);
return flag;
};
总结
我们做题的时候一定要多问为什么,既要知其然,又要知其所以然,否则你面试的时候,说不一定哪一步你没理解,就忘了,结果功亏一篑。所以不要有侥幸心理。