leetcode题目https://leetcode.com/problems/validate-binary-search-tree/,判断一个二叉树是不是合法的二分查找树,这个题目应该说遇到多次了,然而在写的时候还是费了一番时间,而且写了一个十分拙劣的算法(在本文最后,不做解释)。
参考leetcode的discuss,遇到了两个比较好的解法,一个是递归(同样是递归,人家的就很简单),另一个非递归。
先看递归,递归的话采用自顶向下的方式比较简单,自顶向下,也就是自父节点向子节点传递范围要求(子节点不能大于某个值,不能小于某个值),比自底向上要方便的多,看代码,采用Long.MIN_VALUE是防止根节点是Integer.MIN_VALUE,那就通不过了:
/* 判断一个二叉树是不是合法的二分查找树的简单的递给方法,学习
* 采用自顶向下的遍历方式,对于每个节点,检查顶部传来的范围要求,
* 要求是指:对于左子树,父节点的值就是最大值,对于右子树,父节点的值就是最小值
*/
public boolean isValidBST(TreeNode root) {
//初始的时候,对根节点没有范围要求
return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
public boolean isValidBST(TreeNode root, long minVal, long maxVal) {
if (root == null) return true;
//检查是否满足根节点的范围要求
if (root.val >= maxVal || root.val <= minVal)
return false;
//修改对子节点的要求,对于左子树,本节点的值就是最大值,对于右子树,本节点的值就是最小值
return isValidBST(root.left, minVal, root.val) && isValidBST(root.right, root.val, maxVal);
}
再看
非递归,非递归的方式比递归的方式速度要慢,但是很明显非递归能有效的减小栈空间的使用量,防止栈溢出。思路是使用中序非递归遍历(
这里有二叉树的非递归中序遍历),跟中序非递归遍历基本完全一致,就是保存了一个前驱节点,并在每次访问一个节点的时候更新前驱节点,如果前驱节点的值大于等于当前节点,那就是非二分查找树,因为二分查找树的中序遍历是一个
递增序列。看代码:
/*判断一个二叉树是不是合法的二叉树的非递归遍历
* 采用中序遍历,并保存一个前驱节点,这样在每检查一个
* 节点的时候,就跟前驱节点对比,如果比前驱节点小(或者等于)
* 就表示不合法
*/
public boolean isValidBST(TreeNode root){
Stack<TreeNode> stack = new Stack<TreeNode>();
//设置前驱节点
TreeNode pre = null;
while(root!=null || !stack.isEmpty()){
while(root!=null){ //将当前节点,以及左子树一直入栈,循环结束时,root==null
stack.push(root);
root = root.left;
}
root = stack.pop();
//比较并更新前驱,与普通遍历的区别就在下面四行
if(pre!=null && root.val<= pre.val){
return false;
}
pre = root;
root = root.right; //访问右子树
}
return true;
}
我的拙劣的算法:
public boolean isValidBST(TreeNode root) {
if(root==null){
return true;
}
int[] minMax = new int[2];
return minMax(root, minMax);
}
public boolean minMax(TreeNode root,int[] minMax){
if(root!=null&&root.left==null&&root.right==null){
minMax[0] = root.val;
minMax[1] = root.val;
return true;
}
int[] left = null;
int[] right = null;
if(root.left!=null){
left = new int[2];
boolean bin = minMax(root.left, left);
if(!bin){
return false;
}
}
if(root.right!=null){
right = new int[2];
boolean bin = minMax(root.right,right);
if(!bin){
return false;
}
}
if((left==null||left!=null&&root.val>left[1])
&&(right==null||right!=null&&root.val<right[0])){
if(left!=null&&right!=null){
minMax[0] = Math.min(Math.min(left[0], right[0]),root.val);
minMax[1] = Math.max(Math.max(left[1], right[1]),root.val);
}else if(left!=null){
minMax[0] = Math.min(left[0],root.val);
minMax[1] = Math.max(left[1],root.val);
}else{
minMax[0] = Math.min(right[0],root.val);
minMax[1] = Math.max(right[1],root.val);
}
return true;
}
return false;
}