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:
解题思路:
这种题目也是面试也是比较爱问的,有数有递归。我们看这个递归的定义,一个树是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,巧妙地求解。