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.
Example 1:
2
/ \
1 3
Binary tree [2,1,3]
, return true.
Example 2:
1
/ \
2 3
Binary tree [1,2,3]
, return false.
这道题总算是自己做出来了。。先贴自己的答案(小麻烦)
if(root == null)
return true;
if(root.left == null && root.right == null)
return true;
else if(root.left != null && root.right != null){
if(isValidBST(root.left) && isValidBST(root.right)){
TreeNode rootTemp1 = root.left;
//检测新增根元素是否大于左子树所有值
while(rootTemp1 != null){
if(rootTemp1.val >= root.val)
return false;
rootTemp1 = rootTemp1.right;
}
//检测新增根元素是否小于右子树所有值
TreeNode rootTemp2 = root.right;
while(rootTemp2 != null){
if(rootTemp2.val <= root.val)
return false;
rootTemp2 = rootTemp2.left;
}
//如果全部符合可以返回true,代表新增根元素符合要求
return true;
}
else
return false;
}
else if(root.left != null){
if(isValidBST(root.left)){
TreeNode rootTemp1 = root.left;
//检测新增根元素是否大于左子树所有值
while(rootTemp1 != null){
if(rootTemp1.val >= root.val)
return false;
rootTemp1 = rootTemp1.right;
}
return true;
}
else
return false;
}
else{
if(isValidBST(root.right)){
//检测新增根元素是否小于右子树所有值
TreeNode rootTemp2 = root.right;
while(rootTemp2 != null){
if(rootTemp2.val <= root.val)
return false;
rootTemp2 = rootTemp2.left;
}
//如果全部符合可以返回true,代表新增根元素符合要求
return true;
}
else
return false;
}
}
基本思路,保证子树是BST的同时,根的值要大于所有左子树的值(通过与左子树的所有右儿子比较),并小于所有右子树的值(通过与右子树的所有左儿子比较)。因为封装方法的思想不是很成熟,所以看起来很不舒服,以后慢慢改正。
这道题关键在于,并不是左右子树都是BST,所求树就是BST(自己好好想想)。网上解法甚多,与我这种比较相似的是:
暴力遍历法
从根节点开始递归,遍历所有的节点。并且在每个节点处,分别遍历其左右子树,判断其左子树的最大值比其小,右子树的最小值比其大。
时间复杂度为O(n^2)。
public class Solution {
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
if (!dfsLeft(root.left, root.val) || !dfsRight(root.right, root.val)) return false;
return isValidBST(root.left) && isValidBST(root.right);
}
public boolean dfsLeft(TreeNode root, int value) {
if (root == null) return true;
if (root.val >= value) return false;
return dfsLeft(root.left, value) && dfsLeft(root.right, value);
}
public boolean dfsRight(TreeNode root, int value) {
if (root == null) return true;
if (root.val <= value) return false;
return dfsRight(root.left, value) && dfsRight(root.right, value);
}
}
封装的很简洁,但是运行起来应该比博主本人的稍慢一些(虽然两者都是O(N方))因为暴力遍历每次增加根节点的时候还将根节点与子树所有的儿子比较(博主方法只比较左子树的右儿子和右子树的左儿子)有些多余。
比较牛的方法之一:
失效O(n)解法:最大最小值法
网上很用人用了Integer.MIN_VALUE和Integer.MAX_VALUE来辅助解决这道题,即遍历时记录一个当前允许的最大值和最小值。
public class Solution {
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
if (root.left == null && root.right == null) return true;
return validate(root, Integer.MIN_VALUE, Integer.MAX_VALUE);
}
public boolean validate(TreeNode root, int min, int max) {
if (root == null) return true;
if (root.val <= min || root.val >= max) return false;
return validate(root.left, min, root.val) && validate(root.right, root.val, max);
}
}
但是现在的LeetCode已经更新了这道题,下面这种解法已经通不过了,因为LeetCode多了两个测试用例:
Input: | {-2147483648,#,2147483647} |
Output: | false |
Expected: | true |
正确O(n)解法:中序遍历法
public class Solution {
List<Integer> list = new ArrayList<Integer>();
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
if (root.left == null && root.right == null) return true;
inOrderTraversal(root);
for (int i = 1; i < list.size(); i++) {
if (list.get(i) <= list.get(i - 1)) return false;
}
return true;
}
public void inOrderTraversal(TreeNode root) {
if (root == null) return;
inOrderTraversal(root.left);
list.add(root.val);
inOrderTraversal(root.right);
}
}
二叉搜索树中序遍历是一个有序集合。
最高端解法:(不需额外O(N)空间)
public class Solution {
// Keep the previous value in inorder traversal.
TreeNode pre = null;
public boolean isValidBST(TreeNode root) {
// Traverse the tree in inorder.
if (root != null) {
// Inorder traversal: left first.
if (!isValidBST(root.left)) return false;
// Compare it with the previous value in inorder traversal.
if (pre != null && root.val <= pre.val) return false;
// Update the previous value.
pre = root;
// Inorder traversal: right last.
return isValidBST(root.right);
}
return true;
}
}
通过定义一个方法外的pre记录每次中序遍历的值,一旦下一个值比上一个值小,即退出循环返回false。