Idiot-maker

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

https://oj.leetcode.com/problems/validate-binary-search-tree/

Given a binary tree, determine if it is a valid binary search tree (BST).

Assume a BST is defined as follows:

  • The left subtree of a node contains only nodes with keys less than the node's key.
  • The right subtree of a node contains only nodes with keys greater than the node's key.
  • Both the left and right subtrees must also be binary search trees.

解题思路:

这种题目也是面试也是比较爱问的,有数有递归。我们看这个递归的定义,一个树是BST,必须它的val比左子树所有的val都大,比右子树所有val都小,并且,他的左右子树也都是BST。

你看看,特别注意,是比左子树所有节点都大,比右子树所有的都小。递归的时候,不能光判断root.val > left.val就return true。否则下面的例子

                 5

              /       \

          1            9

                    /      \

                  3        10

实际他不是一个BST,因为3<5。但上面的方法判断不出。

我还是把这个想当然的写法写出来,警示自己。

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public boolean isValidBST(TreeNode root) {
        if(root == null){
            return true;
        }
        if(root.left != null){
            if(root.left.val >= root.val){
                return false;
            }
        }
        if(root.right != null){
            if(root.right.val <= root.val){
                return false;
            }
        }
        return isValidBST(root.left) && isValidBST(root.right);
    }
}

那一个brute force的方法,上面只比较root.left,那我遍历每个节点的时候,都取得他所有左子树节点的集合,与右子树节点的集合。也就是插入一个BFS或者DFS的算法,我这里用BFS,就可以了。

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public boolean isValidBST(TreeNode root) {
        if(root == null){
            return true;
        }
        if(root.left != null){
            List<TreeNode> leftNodeList = new ArrayList<TreeNode>();
            Queue<TreeNode> queue = new LinkedList<TreeNode>();
            queue.offer(root.left);
            while(queue.size() > 0){
                TreeNode temp = queue.poll();
                if(temp.left != null){
                    queue.offer(temp.left);
                }
                if(temp.right != null){
                    queue.offer(temp.right);
                }
                leftNodeList.add(temp);
            }
            for(TreeNode temp: leftNodeList){
                if(temp.val >= root.val){
                    return false;
                }
            }
        }
        if(root.right != null){
            List<TreeNode> rightNodeList = new ArrayList<TreeNode>();
            Queue<TreeNode> queue = new LinkedList<TreeNode>();
            queue.offer(root.right);
            while(queue.size() > 0){
                TreeNode temp = queue.poll();
                if(temp.left != null){
                    queue.offer(temp.left);
                }
                if(temp.right != null){
                    queue.offer(temp.right);
                }
                rightNodeList.add(temp);
            }
            for(TreeNode temp: rightNodeList){
                if(temp.val <= root.val){
                    return false;
                }
            }
        }
        return isValidBST(root.left) && isValidBST(root.right);
    }
}

这是一个AC的解法。但是用到了O(n)的空间,时间复杂度也上去了。下面考虑简化这个方法。

想象一个二叉树,当前节点必然比左子树的任何节点都大,也就是在左子树的所有节点中,该节点是最大的一个。反过来,在右子树所有节点中,该节点也是最小的一个。否则,这个数就不是BST。这也是一个递归的定义,那么我们可以把当前节点的val带进递归。代码如下。

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public boolean isValidBST(TreeNode root) {
        return validBST(root, Long.MAX_VALUE, Long.MIN_VALUE);
    }
    
    public boolean validBST(TreeNode root, long maxVal, long minVal){
        if(root == null){
            return true;
        }
        if(root.val >= maxVal){
            return false;
        }
        if(root.val <= minVal){
            return false;
        }
        return validBST(root.left, root.val, minVal) && validBST(root.right, maxVal, root.val);
    }
}

可以看到,这个方法比较简洁,理解后也比较简单。与上面的递归不同,这个方法不是比较root.left和root的值,已经root.right和root的值,是比较root的值与缓存下来的最大、最小值。这个缓存的值,是通过递归传下来的。每递归一个左子树,就用父节点去更新当前的最大值,递归右子树,更新最小值。

下面还有一个思路完全不同的解法。回忆 Binary Search Tree Iterator 这道题,如果一个数为BST,那么对他进行inorder的遍历,输出一定是一个排好序的数列。那么,这里我们就可以利用这个性质,一次性对这个数inorder遍历,将结果存在一个list里,然后从头往后判断,结果递增,就可以返回true。

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public boolean isValidBST(TreeNode root) {
        List<Integer> list = inOrderTravese(root);
        long temp = Long.MIN_VALUE;
        for(Integer i : list){
            if(i <= temp){
                return false;
            }
            temp = i;
        }
        return true;
    }
    
    public List<Integer> inOrderTravese(TreeNode root){
        List<Integer> list = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        
        while(root != null || stack.size() !=0){
            if(root != null){
                stack.push(root);
                root = root.left;
            }else{
                root = stack.pop();
                list.add(root.val);
                root = root.right;
            }
        }
        return list;
    }
}

总结一下,这道题一看便是一道递归的解法,但是要咬准递归的定义,还是需要了解一下的。特别是Integer.MIN_VALUE的边界,当然还有MAX_VALUE,我们可以初始化为Long.MIN_VALUE。第二种是一个很巧妙地解法,不straight-forward,但是一提出来就立刻可以理解了。他利用了BST的inorder遍历输出是一个排序数列的方法,用空间换时间,two-passs solution,巧妙地求解。

posted on 2015-02-27 20:35  NickyYe  阅读(266)  评论(0编辑  收藏  举报