给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
分析
这道题可以用前中后序三种解法,并且需要了解二叉搜索数的性质,是一道不错的题,记录一下。
一、前序遍历解法
如图所示,我们考虑上图中的二叉树是否为二叉搜索树,按照前序遍历的解法,首先需要判断当前根节点是否在合适的区间范围内,然后递归判断左右子树,**递归左子树时把根节点的值作为区间上界,因为左子树上的所有节点都应小于根节点的值;递归右子树时把根节点的值作为区间下界,因为右子树上的所有节点都应小于根节点的值。**当根节点和左右子树都符合二叉搜索树的性质时,该树为二叉搜索树。
代码实现如下:
class Solution {
public boolean isValidBST(TreeNode root) {
return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
public boolean isValidBST(TreeNode node, long lower, long upper) {
if (node == null) {
return true;
}
if (node.val <= lower || node.val >= upper) {
return false;
}
return isValidBST(node.left, lower, node.val) && isValidBST(node.right, node.val, upper);
}
}
二、中序遍历解法
对于一棵二叉搜索树,其中序遍历结果一定是升序的。
根据上面的性质,在中序遍历时我们可以判断中序遍历结果是否升序,判断是否升序的方法即为判断当前节点的值是否大于前一个节点的值。
代码实现如下:
class Solution {
long pre = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
return helper(root);
}
public boolean helper(TreeNode node) {
if (node == null) {
return true;
}
boolean left = helper(node.left);
if(node.val <= pre) return false;
pre = node.val;
boolean right = helper(node.right);
return left && right;
}
}
三、后序遍历解法
在前序遍历中我们是把节点值的范围往下传,其实我们还可以把节点值的范围往上传,这对应的就是后序遍历。
在进行后序遍历时,我们向根节点返回子树的最小值和最大值long[2],其中long[0]表示最小值,long[1]表示最大值
遇到空节点我们返回[+∞,-∞],这样可以使空节点的根节点一定满足二叉搜索树的条件。
在非空节点,如果根节点小于左子树的最大值或者大于右子树的最小值(即不满足二叉搜索树的条件),就返回[-∞,+∞],这样可以保证上一级根节点一定会不满足二叉搜索树的条件
最后判断根节点范围的右边界是否为+∞,如果是返回false,不是返回true
class Solution {
public boolean isValidBST(TreeNode root) {
return dfs(root)[1] != Long.MAX_VALUE;
}
private long[] dfs(TreeNode node) {
if (node == null)
return new long[]{Long.MAX_VALUE, Long.MIN_VALUE};
long[] left = dfs(node.left);
long[] right = dfs(node.right);
long x = node.val;
if (x <= left[1] || x >= right[0])
return new long[]{Long.MIN_VALUE, Long.MAX_VALUE};
return new long[]{Math.min(left[0], x), Math.max(right[1], x)};
}
}