题目描述
leetcode 98题
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
思路
必然从二叉搜索树的特性出发。容易知道,二叉搜索树的中序遍历是一个递增的数组。所以最简单的方法就是对其进行中序遍历,并看是否是递增的。
1. 记录中序遍历结果
class Solution {
// 存储中序遍历的结果
private List<Integer> li = new ArrayList<>();
public boolean isValidBST(TreeNode root) {
inOrder(root);
// 判断是否递增
for(int i = 1;i < li.size();i++){
if(li.get(i) <= li.get(i-1)) return false;
}
return true;
}
public void inOrder(TreeNode root){
// 中序遍历
if(root == null) return;
inOrder(root.left);
li.add(root.val);
inOrder(root.right);
}
}
这种方法的缺点,第一是需要一个O(n)
的空间来存储中序遍历的结果;第二是如果遍历过程中已经发现不满足二叉搜索树的情况了,其实可以直接退出,但是它要第二次遍历时才会退出。
2. 与前一个比较(递归)
对中序遍历的方法进行略微修改即可。由于中序遍历是按照从小到大的顺序的,所以只要当前值小于或等于前一个的值,那么就必然是false
。所以用pre
记录前一个的值即可,但是要注意pre
的初始化。
class Solution {
private long pre = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
return inOrder(root);
}
public boolean inOrder(TreeNode root){
if(root == null) return true;
boolean l = inOrder(root.left);
int cur = root.val;
if(pre >= cur){
return false;
}
// 当前 value 变成pre value
pre = cur;
boolean r = inOrder(root.right);
// 必须满足两侧都是搜索树
return l && r;
}
}
对pre
用Long
来初始化,因为测试用例中有Integer.MIN_VALUE
,所以如果pre
也是Integer.MIN_VALUE
的话,无法大于它。
3. 与前一个比较(非递归)
思想同理,用指针和堆栈实现二叉树的非递归中序遍历。不过效率居然比第二个要低,不知道为什么。
class Solution {
public boolean isValidBST(TreeNode root) {
if(root == null) return true;
Stack<TreeNode> s = new Stack<>();
TreeNode cur = root;
long pre = Long.MIN_VALUE;
while(!s.isEmpty() || cur != null){
if(cur != null){
s.push(cur);
cur = cur.left;
}else{
cur = s.pop();
if(pre >= cur.val) return false;
pre = cur.val;
cur = cur.right;
}
}
return true;
}
}
4. 划定界限
在leetcode上,很多人的解法是从界限出发的。因为左子树的值都是小于root的,右子树的值都是大于root的,所以左侧的范围就是(min,root.val)
,右侧的范围是(root.val,max)
。但是要注意边界的问题,到底有没有等号。
不过这种思路并没有利用到二叉搜索树的中序遍历是有序的这个特性。
class Solution {
public boolean isValidBST(TreeNode root) {
return isValidBST(root,Long.MIN_VALUE,Long.MAX_VALUE);
}
public boolean isValidBST(TreeNode root, long min, long max) {
if(root == null) return true;
// 这里判断是即使==也返回false,所以min和max是开区间。(*)
if(root.val <= min || root.val >= max){
return false;
}
// 由于(*),所以这里不需要root.val+1或者-1
return isValidBST(root.left,min,root.val)
&& isValidBST(root.right,root.val,max);
}
}