「这是我参与2022首次更文挑战的第19天,活动详情查看:2022首次更文挑战」。
描述
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效二叉搜索树定义如下: 1
-
节点的左子树只包含 小于 当前节点的数。
-
节点的右子树只包含 大于 当前节点的数。
-
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:root = [2,1,3]
输出:true
示例 2:
输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。
提示:
树中节点数目范围在[1, 104] 内
-231 <= Node.val <= 231 - 1
通过次数395,207 提交次数1,120,288
做题
主要围绕着题目的有效二叉树定义来做就行了。
当然不可避免地要把整棵二叉树给遍历一遍,那我们的遍历方法有两五种:深度优先、广度优先,还有前中后序遍历,我们能使用的是深度优先、广度优先、中序遍历。
其中,深度优先和广度优先的处理逻辑基本上是一样的。
深度优先/递归法
从根节点开始,接着递归左子节点、右子节点。
一开始还以为,只需要判断当前节点的值是否大于左字节点的值,是否小于右子节点的值就行了,结果是要当前节点的左子节点只能包含比当前节点小的值,是左边整一棵树,而不只是一个左子节点。
结果,都写完了,提交不通过。
错误代码贴一下。
public boolean isValidBST(TreeNode root) {
if(root == null || (root.left == null && root.right == null)){
//如果当前节点 or 当前节点的左右子节点为 null,不需要判断
return true;
}
if(root.left != null && root.left.val >= root.val || root.right != null && root.right.val <= root.val){
// 不符合条件
return false;
}
return isValidBST(root.left) && isValidBST(root.right);
}
复制代码
需要满足以下条件:
- 左节点小于当前节点 & 右节点大于当前节点。
- 左子树的所有节点在小于当前节点。
- 右子树的所有节点大于当前节点。
第2,3点,举个例子,这里左框里的节点要小于 5,右框里的节点要大于 5。
那左框的节点要大于一个什么数?右框的节点又要小于一个什么数?
不需要去设置,左框里的节点要小于 5,右框里的节点要大于 5 就可以了。
因为根节点可以是任何数,所以我们可以先去判断根节点是否大于 Long.MIN_VALUE,是否小于 Long.MAX_VALUE,这里就确定了下界和上界,只需要它是一个数就可以满足。
根节点的左子树必须小于根节点的值,所以左子树的上界是根节点的值,下界是 Long.MIN_VALUE。
根节点的右子树必须大于根节点的值,所以右子树的下界是根节点的值,上界是 Long.MAX_VALUE。
一直递归这个操作,就可以判断是否是一棵有效的二叉搜索树了。
(这就意味着,广度优先是不可行的了)
public boolean isValidBST(TreeNode root) {
return recurse(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
private boolean recurse(TreeNode node,long min,long max){
if(node == null){
return true;
}
if(node.val <= min || node.val >=max){
return false;
}
return recurse(node.left,min,node.val) && recurse(node.right,node.val,max);
}
复制代码
中序遍历
要验证二叉搜索树,最好的做法就是使用中序遍历。
中序遍历的二叉搜索树的数是刚好从小到大排序(升序)的,我们只需要在遍历的时候取比对当前节点的数是否比上一个节点的大就行了,思路更加简单。
这里先回顾一下,怎么使用递归法实现中序遍历(迭代法目前还没学会哈哈)
我们只需要把红框框住的代码换成我们对比节点数的代码,还有两次递归 recurse 返回的结果都是有作用的,所以要用一个判断来返回第一个递归的结果。
class Solution {
public boolean isValidBST(TreeNode root) {
List<Integer> list = new ArrayList();
return recurse(root, list);
}
public boolean recurse(TreeNode node,List<Integer> list) {
if (node == null) {
return true;
}
if(!recurse(node.left,list)){
return false;
}
if(!list.isEmpty()){
//获取前一个节点的值
Integer pre = list.get(list.size()-1);
if(node.val<=pre){
return false;
}
}
list.add(node.val);
return recurse(node.right,list);
}
}
复制代码
执行事件基本上都是 0ms,内存占用会比较大,我看有些朋友使用一个类变量来存储 pre,那样也是可以,内存占用应该会小一点。
最后
今天就到这里了。
这里是程序员徐小白,【每日算法】是我新开的一个专栏,在这里主要记录我学习算法的日常,也希望我能够坚持每日学习算法,不知道这样的文章风格您是否喜欢,不要吝啬您免费的赞,您的点赞、收藏以及评论都是我下班后坚持更文的动力。