Leetcode98.验证二叉搜索树 Validate Binary Search Tree(Java)
##Tree##, ##Depth-first Search##, ##Recursion##
验证二叉搜索树:给定一个二叉搜索树,判断其是否是一个有效的二叉搜索树
二叉搜索树的定义(特征)
- 节点的左子树只包含小于当前节点的数
- 节点的右子树只包含大于当前节点的数
- 所有左子树和右子树自身必须也是二叉搜索树
二叉搜索树中不能存在值相同的结点
方法一
二叉搜索树的中序遍历是有序序列
ArrayList
记录二叉搜索树的中序遍历结果,在遍历ArrayList
中数的排列是否有序
时间复杂度: O(n)
class Solution {
public ArrayList<Integer> nums = new ArrayList<>();
public boolean isValidBST(TreeNode root) {
dfs(root);
for (int i = 1; i < nums.size(); i ++) {
if (nums.get(i) <= nums.get(i - 1)) return false;
}
return true;
}
public void dfs(TreeNode root) {
if (root == null) return;
dfs(root.left);
nums.add(root.val);
dfs(root.right);
}
}
方法二
对于一棵树,根节点为root
,递归判断
- 树的左子树是二叉搜索树
- 树的右子树是二叉搜索树
- 树根节点的值应大于左子树的最大值
root.val > leftMax
,且树根节点的值应小于右子树的最小值root.val > rightMin
第三条判断不能用 根据根节点与左右儿子结点的值判断结果root.val > root.left.val && root.val < root.right.val
来代替
考虑到递归每一个结点都需要返回三个变量(1.以该结点为根的树是否为二叉搜索树;2.以该结点为根的树的最大值;3.以该结点为根的树的最大值),将函数返回值设定为返回一个数组
数组维护的三个变量:1.以该结点为根的树是否为二叉搜索树;2.以该结点为根的树的最大值;3.以该结点为根的树的最大值
时间复杂度: O(n)
class Solution {
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
return dfs(root)[0] == 1;
}
public int[] dfs(TreeNode root) {
int[] res = {1, root.val, root.val};
if (root.left != null) {
int[] temp = dfs(root.left);
if (temp[0] == 0 || temp[2] >= root.val) res[0] = 0;
res[1] = Math.min(res[1], temp[1]);
res[2] = Math.max(res[2], temp[2]);
}
if (root.right != null) {
int[] temp = dfs(root.right);
if (temp[0] == 0 || temp[1] <= root.val) res[0] = 0;
res[1] = Math.min(res[1], temp[1]);
res[2] = Math.max(res[2], temp[2]);
}
return res;
}
}
方法三
综合考虑方法一和方法二,采用中序遍历方法,设置变量long maxpre
记录上一次遍历结点的值,根据二叉搜索树中序遍历单调性质,maxpre
的中应该是所有已被遍历过的结点的最大值
因为采用中序遍历,所以maxpre
应该是单调递增的,当存在root.val <= maxpre
时(正在遍历的结点值小于等于之前遍历过的结点的值,不符合二叉搜索树中序遍历有序的性质),代表不符合二叉搜索树
将变量类型设置为long
是因为存在值为Integer.MIN_VALUE
的结点
时间复杂度: O(n)
class Solution {
Long pre = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
boolean left = isValidBST(root.left);
if (pre >= root.val) return false;
pre = (long)root.val;
boolean right = isValidBST(root.right);
return left && right;
}
}
如果节点中存在Long.MAX_VALUE
时,可以记录上一次遍历的变量设置为TreeNode pre
,这样根据有普适性
class Solution {
TreeNode pre = null;
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
boolean left = isValidBST(root.left);
if (pre != null && pre.val >= root.val) return false;
pre = root;
boolean right = isValidBST(root.right);
return left && right;
}
}
方法四
树的根节点为root
-
左子树的所有结点的取值应该在(-∞,root.val)
开区间
之间 -
同理,右子树的所有结点的取值应该在(root.val,+∞)之间
递归判断所有结点是否满足上述两条性质,若满足则为二叉搜索树
注意变量设置为Long
时间复杂度: O(n)
class Solution {
public boolean isValidBST(TreeNode root) {
return dfs(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
public boolean dfs(TreeNode root, long min, long max) {
if (root == null) return true;
if (root.val <= min || root.val >= max) return false;
else return dfs(root.left, min, root.val) && dfs(root.right, root.val, max);
}
}