题目要求很简单:给定一个二叉树,写一个算法判断是否为二叉搜索树。
重新回顾一下二叉搜索树BST的定义:
如果一个二叉树为二叉搜索树那么:
1.左子树只包含比根节点值要小的节点。
2.右子树只包含比根节点值要大的节点。
3.左子树右子树都为二叉搜索树。
很显然这是一个递归定义,那么解决这个问题的方法自然也应该是用递归去做。所以很快有了第一版代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public boolean isValidBST(TreeNode root) {
return isValid(root);
}
private boolean isValid(TreeNode root){
if(root == null){
return true;
}
if(root.left != null && root.left.val >= root.val){
return false;
}
if(root.right != null && root.right.val <= root.val){
return false;
}
else{
if(isValid(root.left)&&isValid(root.right)){
return true;
}
return false;
}
}
写完还洋洋得意了一下尼玛第一次这么丝滑的写了一个recursion。然后提交,结果在这个测试用例上傻逼了:[10,5,15,null,null,6,20]。 我的递归只检查了左右字数与根节点的值的大小。但是BST还有一个重要的属性就是根节点的左子树的最大值一定比根节点小,右子树的最小值比根节点要大。我的递归程序明显没有考虑到这一点。
修改后提交第二版代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public boolean isValidBST(TreeNode root) {
return isValid(root);
}
private boolean isValid(TreeNode root){
if(root == null){
return true;
}
if(root.left != null && getMax(root.left) >= root.val){
return false;
}
if(root.right != null && getMin(root.right) <= root.val){
return false;
}
if(!isValid(root.left) || !isValid(root.right)){
return false;
}
else{
return true;
}
}
private int getMax(TreeNode root){
if(root == null){
return Integer.MIN_VALUE;
}
int left = getMax(root.left);
int right = getMax(root.right);
return Math.max(Math.max(left,right),root.val);
}
private int getMin(TreeNode root){
if(root == null){
return Integer.MAX_VALUE;
}
int left = getMin(root.left);
int right = getMin(root.right);
return Math.min(Math.min(left,right),root.val);
}
增加了两个getMin和getMax的方法寻找当前根节点下的最大最小值。AC。但是我每一个递归的时候都会调用getMax getMin函数 isVlid是O(n) getMax getMin也是O(n)所以一共是O(n^2)效率非常非常的低,从AC的运行时间也能看出来这一点。
最后通过查询资料写了第三种方法,不得不说第三种方法非常的巧妙:
public class Solution {
public boolean isValidBST(TreeNode root) {
return isValid(root,null,null);
}
private boolean isValid(TreeNode root,Integer Min,Integer Max){
if(root == null){
return true;
}
if(Min != null && root.val <= Min){
return false;
}
if(Max != null && root.val >= Max){
return false;
}
return isValid(root.left,Min,root.val) && isValid(root.right,root.val,Max);
}
}
第三种方法的巧妙之处在于通过限定了Min以及Max的范围使得遍历一次树就可以得到最大以及最小值,效率非常的高。
第四种方法就比较简单直接:利用二叉搜索树的中序遍历序列的结果是一个递增数组这个属性来判断,如果数组不递增,则这个二叉树不为二叉搜索树。
public class Solution {
/**
* @param root: The root of binary tree.
* @return: True if the binary tree is BST, or false
*/
public boolean isValidBST(TreeNode root) {
// write your code here
ArrayList<Integer> check = new ArrayList<Integer>();
INODER(root,check);
for(int i = 0;i<check.size()-1;i++){
if(check.get(i)>=check.get(i+1)){
return false;
}
}
return true;
}
private void INODER(TreeNode root , ArrayList<Integer> check){
if(root == null){
return;
}
INODER(root.left,check);
check.add(root.val);
INODER(root.right,check);
}
}
树的各个问题,由于树的属性,基本上都是通过递归解决。感觉自己还是很难一次性写出正确的递归算法,路漫漫,还是需要多多练习。