给你一个二叉树的根节点 root
,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
- 节点的左
子树
只包含 小于 当前节点的数。 - 节点的右子树只包含 大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
示例 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
思路:
先确定一下分析方法,这个乍一看就看出了解决办法,就先理出一个大致思路,然后实现,因为有很多小细节,小问题可能是想而不到的,所以就需要运行,发现问题,再打补丁,使用这个分析方法可以让目的比较明确,效率也比较高。
回归到本题,题目一看就是循环或者递归遍历,应该都是能够实现的,当需要遍历的分值多的时候,循环就显得比较复杂了,所以这道题我推荐递归。当然这道题也是用递归实现的,建立两个递归的分支函数,分别问检验左子树和检验右子树的函数,先分析一边,检验成功不好理解,先看怎么叫做检验失败然后再取反。
对于左子树如果他其中有一个节点大于现在的父节点,就认为是检验失败的,这是题目中给的定义,看起来还是很朦胧,那么再细化,分析下一左右子树,左子树的左子树的左节点不能大于左子树的节点值,但是右子树就麻烦了,它不仅要大于左子树的节点值,而且要小于左子树的父节点值,上图
就是3节点不仅要考虑和4的关系,更要考虑和5的关系,比5大比4小,显然不成立为false,举个例子而已,因为这个例子在4的时候就已经被淘汰了。
所以传参的时候不仅要传节点这个必须的,还有root.val和父节点的值,在左子树下,root.val就是max,min看左子树的父节点是不是右子树。在右子树下也一样,然后上代码。
代码:
/**
* 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) {
// 一看树 一般就两大解决办法 循环+队列 或 递归
// 先考虑递归 显然普通递归只能判断三个节点间是否符合二叉搜索树
// 导致这个问题 一共两个情况就是 左子树的右节点可能大于根节点
// 或者右子树的左节点可能小于根节点
return verify0(root.left, root.val, null) && verify1(root.right, root.val, null);
}
public boolean verify0(TreeNode root, int max, Integer min) {
if (root == null) return true;
if (root.val >= max) return false;
// 验证左子树 min传null的话就是没有最小限制,这种情况就是左子树的左节点
if (min != null && root.val <= min) return false;
return verify0(root.left, root.val, min) && verify1(root.right, root.val, max);
}
public boolean verify1(TreeNode root, int min, Integer max) {
// 验证右子树
if (root == null) return true;
if (root.val <= min) return false;
// 验证右子树 max传null的话就是没有最大限制,这种情况就是右子树的右节点
if (max != null && root.val >= max) return false;
return verify0(root.left, root.val, min) && verify1(root.right, root.val, max);
}
}
运行结果: