给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:
2
/ \
1 3
输出: true
示例 2:
输入:
5
/ \
1 4
/ \
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
解答该题要注意不仅右子节点要大于该节点,整个右子树的元素都应该大于该节点
方法一:递归
思路:在遍历树的同时保留节点的上界与下界,比较时要同时考虑子节点和上下界的值
- 将节点的值与上界和下界进行比较
- 根据节点的值对左子树右子树的上界下届分别进行更新
- 左子树和右子树递归完成该过程
时间复杂度:O(N) 每个节点访问一次,判断该节点是否小于上界及右子节点,大于下界及左子节点。
空间复杂度:O(N) 跟进整个树
public boolean help_isValidBST(TreeNode node,Integer lower,Integer upper) {
if (node==null) return true;
int val=node.val;
System.out.println(lower+" "+val+" "+upper);
if(val<=lower) return false;
if(val>=upper) return false;
if (!help_isValidBST(node.right,val,upper)) return false;
if (!help_isValidBST(node.left,lower,val)) return false;
return true;
}
public boolean isValidBST0(TreeNode node) {
return help_isValidBST(node,Integer.MIN_VALUE,Integer.MAX_VALUE);
}
方法二:迭代
思路:利用队列先进先出的特点,迭代进行广度优先遍历
- 将t层的元素从右向左依次入队
- 将t层元素逐个出队,根据其上下界值判定是否满足二叉搜索树(必要条件)
- 将其子节点入队,同时对入队元素的上下界值进行更新
时间复杂度:O(N) 每个节点访问一次
空间复杂度:O(N) 跟进整个树
public void update(TreeNode node,Integer lower,Integer upper,
Queue<TreeNode> queue,Queue<Integer> lowers,Queue<Integer> uppers) {
queue.add(node);
lowers.add(lower);
uppers.add(upper);
if (node!=null)
System.out.println(lower+" "+node.val+" "+upper+" "+queue.size());
}
public boolean isValidBST1(TreeNode node) {
Queue<TreeNode> queue=new LinkedList<TreeNode>();
Queue<Integer> uppers=new LinkedList<Integer>(),
lowers=new LinkedList<Integer>();
Integer lower=Integer.MIN_VALUE, upper=Integer.MAX_VALUE,val;
update(node,lower,upper,queue,lowers,uppers);
while(!queue.isEmpty()) {
node=queue.poll();
lower=lowers.poll();
upper=uppers.poll();
if(node==null) continue;
val=node.val;
if(val<=lower) return false;
if(val>=upper) return false;
update(node.right,val,upper,queue,lowers,uppers);
update(node.left,lower,val,queue,lowers,uppers);
}
return true;
}
方法三:
思路:利用栈后进先出的特点,通过中序遍历实现深度优先遍历
- 找到当前节点t最左的分支t_left,进行判定,对其进行中序遍历left-inorder-right及判定
- 节点t_left出栈,判定t节点,并对t进行中序遍历及判定
注意:
- 中序遍历对于二叉搜索树即可得到由小到大的数
- t_left为最左分支,即二叉搜索树的最小值
- 判定过程仅需大于前一个值即可
public boolean isValidBST2(TreeNode node) {
Stack<TreeNode> stack=new Stack();
double inorder=-Double.MAX_VALUE;
while(!stack.isEmpty() || node!=null) {
while(node!=null) {
stack.push(node);
node=node.left;
}
node=stack.pop();
if (node!=null)
System.out.println(inorder+" "+node.val+" "+stack.size());
if(node.val<=inorder) return false;
inorder=node.val;
node=node.right;
if (node!=null)
System.out.println(node.val);
}
return true;
}
给一个大牛的代码:
思路:利用中序遍历,仅需一次递归调用
- 基于二叉树的中序遍历-->从小到大遍历
- 定义一个变量存储上一遍历节点的值-->x下界
- 递归遍历二叉树
- 左子树返回true,对比根结点与缓存变量
- 若4成立,根结点的值赋给缓存变量
- 递归遍历右子树
double last=-Double.MAX_VALUE;
public boolean isValidBST(TreeNode root) {
if(root==null)
return true;
if(isValidBST(root.left)) {
System.out.println(last+" "+root.val);
if(last<root.val) {
last=root.val;
return isValidBST(root.right);
}
}
return false;
}