标题:Java实现判断一棵树是否为BST二叉搜索树
题目:判断是否为二叉搜索树
98. 验证二叉搜索树
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:
2
/ \
1 3
输出: true
示例 2:
输入:
5
/ \
1 4
/ \
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
/*方法一:递归,使用long 【相当于补全BST】
执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:37.7 MB, 在所有 Java 提交中击败了97.73% 的用户
*/
public int isValidBST(TreeNode root) {
return this.preorder(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
public boolean preorder(TreeNode root, long min, long max){
if(root == null){
return true;
}else if(root.val <= min || root.val >= max){
return false;
}else{
boolean b1 = this.preorder(root.left, min, root.val);
boolean b2 = this.preorder(root.right, root.val, max);
return b1 && b2;
}
}
上述的示意图:
/*方法二:递归,使用节点TreeNode,思想同上
执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:38.2 MB, 在所有 Java 提交中击败了69.92% 的用户
*/
public boolean isValidBST(TreeNode root) {
return this.preorder(root, null, null);
}
public boolean preorder(TreeNode root, TreeNode min, TreeNode max){
if(root == null){
return true;
}else if(min != null && root.val <= min.val){
return false;
}else if(max != null && root.val >= max.val){
return false;
}else{
boolean b1 = this.preorder(root.left, min, root);
boolean b2 = this.preorder(root.right, root, max);
return b1 && b2;
}
}
//上述两个方法的非递归遍历 【这个迭代特别有想法】
执行用时:4 ms, 在所有 Java 提交中击败了5.41% 的用户
内存消耗:38.1 MB, 在所有 Java 提交中击败了71.77% 的用户
public boolean isValidBST(TreeNode root){
if(root == null){
return true;
}
Deque<TreeNode> s = new LinkedList<>();
s.push(root);
Deque<Long> minS = new LinkedList<>();
Deque<Long> maxS = new LinkedList<>();
minS.push(Long.MIN_VALUE);
maxS.push(Long.MAX_VALUE);
while(!s.isEmpty()){
TreeNode p = s.pop();
long min = minS.pop();
long max = maxS.pop();
if(p.val <= min || p.val >= max){
return false;
}
if(p.right != null){
s.push(p.right);
minS.push((long)p.val);
maxS.push(max);
}
if(p.left != null){
s.push(p.left);
minS.push(min);
maxS.push((long)p.val);
}
}
return true;
}
/*
方法三:递归:使用一个节点pre --》 使用long pre = Long.MIN_VALUE;
执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:38.1 MB, 在所有 Java 提交中击败了74.91% 的用户
*/
private TreeNode pre;
public boolean isValidBST(TreeNode root) {
return this.preorder(root);
}
public boolean preorder(TreeNode root){
if(root == null){
return true;
}else{
boolean b1 = this.preorder(root.left);
if(pre != null && root.val <= pre.val){
return false;
}
pre = root;
boolean b2 = this.preorder(root.right);
return b1 && b2;
}
}
//非递归实现
执行用时:3 ms, 在所有 Java 提交中击败了8.53% 的用户
内存消耗:37.8 MB, 在所有 Java 提交中击败了95.85% 的用户
public boolean isValidBST(TreeNode root){
if(root == null){
return true;
}
Deque<TreeNode> s = new LinkedList<>();
s.push(root);
TreeNode pre = null;
while(!s.isEmpty()){
TreeNode p = s.peek();
if(p.left != null){
s.push(p.left);
}else{
s.pop();
//sout;
if(pre != null && pre.val >= p.val){
return false;
}
pre = p;
while(p.right == null && !s.isEmpty()){
p = s.pop();
//sout;
if(pre != null && pre.val >= p.val){
return false;
}
pre = p;
}
if(p.right != null){
s.push(p.right);
}else{
break;
}
}
}
return true;
}
/*
方法四:对于每一个node,
要求:right一步,一直向左走,找到leftNode, 要求node.val < leftNode
left一步,一直向右走,找到rightNode, 要求node.val > rightNode
执行结果:
通过
显示详情
执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:38.1 MB, 在所有 Java 提交中击败了78.48% 的用户
*/
public boolean isValidBST(TreeNode root) {
if(root == null){
return true;
}else{
if(root.left != null){
TreeNode leftMax = this.getLeftMax(root.left);
if(root.val <= leftMax.val){
return false;
}
}
if(root.right != null){
TreeNode rightMin = this.getRightMin(root.right);
if(root.val >= rightMin.val){
return false;
}
}
return isValidBST(root.left) && isValidBST(root.right);
}
}
private TreeNode getRightMin(TreeNode root){
while(root.left != null){
root = root.left;
}
return root;
}
private TreeNode getLeftMax(TreeNode root){
while(root.right != null){
root = root.right;
}
return root;
}
/*
方法五:中序得到list,看是否有后面一个val > 前面一个value
执行用时:2 ms, 在所有 Java 提交中击败了30.67% 的用户
内存消耗:38.4 MB, 在所有 Java 提交中击败了51.64% 的用户
*/
public boolean isValidBST(TreeNode root) {
List<Integer> list = new ArrayList<>();
this.inorder(root, list);
for(int i = 1; i < list.size(); i++){
if(list.get(i) <= list.get(i - 1)){
return false;
}
}
return true;
}
private void inorder(TreeNode root, List<Integer> list){
if(root == null){
return ;
}else{
this.inorder(root.left, list);
list.add(root.val);
this.inorder(root.right, list);
}
}
//
boolean isValidBST(TreeNode root) {
return isValidBST(root, null, null);
}
boolean isValidBST(TreeNode root, TreeNode min, TreeNode max) {
if (root == null) return true;
if (min != null && root.val <= min.val) return false;
if (max != null && root.val >= max.val) return false;
return isValidBST(root.left, min, root)
&& isValidBST(root.right, root, max);
}
// 将我的TreeNode 变成了long
long pre = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if(root==null) return true;
if(!isValidBST(root.left)) return false; //访问左子树
if(root.val <= pre) return false; //当前节点是否小于上一节点的值
pre = root.val;
return isValidBST(root.right); //访问右子树
}
//
public boolean isValidBST(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<TreeNode>();
double inorder = -Double.MAX_VALUE;
while (!stack.isEmpty() || root != null) {
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
// 如果中序遍历得到的节点的值小于等于前一个 inorder,说明不是二叉搜索树
if (root.val <= inorder) {
return false;
}
inorder = root.val;
root = root.right;
}
return true;
}
开始一步步的推导
//全
我们从简单的前序递归开始
class Solution {
public boolean isValidBST(TreeNode root) {
return dfs(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
boolean dfs(TreeNode node, long lower, long upper) {
if (node == null)
return true;
if (node.val <= lower)
return false;
if (node.val >= upper)
return false;
if (!dfs(node.left, lower, node.val))
return false;
if (!dfs(node.right, node.val, upper))
return false;
return true;
}
}
写花哨一点是:
class Solution {
public boolean isValidBST(TreeNode root) {
return dfs(root, java.lang.Long.MIN_VALUE, java.lang.Long.MAX_VALUE);
}
boolean dfs(TreeNode node, long min, long max) {
return (node == null) || node.val > min && node.val < max && dfs(node.left, min, node.val) && dfs(node.right, node.val, max);
}
}
如果不用递归,而用栈实现前序: 【写的特别好,这个迭代】
class Solution {
Stack<TreeNode> st = new Stack<>();
Stack<Long> upperList = new Stack<>(),
lowerList = new Stack<>();
public boolean isValidBST(TreeNode root) {
long lower = Long.MIN_VALUE, upper = Long.MAX_VALUE, val;
update(root, lower, upper);
while (!st.empty()) {
root = st.pop();
lower = lowerList.pop();
upper = upperList.pop();
if (root == null) continue;
val = (long)root.val;
if (val <= lower) return false;
if (val >= upper) return false;
update(root.right, val, upper);
update(root.left, lower, val);
}
return true;
}
void update(TreeNode node, long lower, long upper) {
st.push(node);
lowerList.push(lower);
upperList.push(upper);
}
}
仅仅将栈改为队列,就实现了广度优先:
class Solution {
Queue<TreeNode> queue = new LinkedList<>();
Queue<Long> upperList = new LinkedList<>(),
lowerList = new LinkedList<>();
public boolean isValidBST(TreeNode root) {
long lower = Long.MIN_VALUE, upper = Long.MAX_VALUE, val;
update(root, lower, upper);
while (!queue.isEmpty()) {
root = queue.poll();
lower = lowerList.poll();
upper = upperList.poll();
if (root == null) continue;
val = root.val;
if (val <= lower) return false;
if (val >= upper) return false;
update(root.left, lower, val);
update(root.right, val, upper);
}
return true;
}
void update(TreeNode root, long lower, long upper) {
queue.offer(root);
lowerList.offer(lower);
upperList.offer(upper);
}
}
现在换回栈,但改用中序排序
执行用时:2 ms, 在所有 Java 提交中击败了30.67% 的用户
内存消耗:38.1 MB, 在所有 Java 提交中击败了80.63% 的用户
class Solution {
public boolean isValidBST(TreeNode root) {
Stack<TreeNode> st = new Stack<>();
long lastVal = Long.MIN_VALUE;
while (!st.empty() || root != null) {
while(root != null) {
st.push(root);
root = root.left;
}
root = st.pop();
if (root.val <= lastVal) return false;
lastVal = (long)root.val;
root = root.right;
}
return true;
}
}
用递归来实现中序,往上翻看看和前序递归有什么区别,想一想为什么中序只需判断一次节点值的大小
class Solution {
long lastVal = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
return dfs(root);
}
boolean dfs(TreeNode node) {
if (node == null)
return true;
if (!dfs(node.left))
return false;
if (node.val <= lastVal)
return false;
lastVal = (long) node.val;
if (!dfs(node.right))
return false;
return true;
}
}
dfs和isValidBST 在参数返回值一致,可以把这两个合并为一个
class Solution {
long lastVal = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if (root == null)
return true;
if (!isValidBST(root.left))
return false;
if (root.val <= lastVal)
return false;
lastVal = (long) root.val;
if (!isValidBST(root.right))
return false;
return true;
}
}
写得花哨一点就只剩一行
class Solution {
long lastVal = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
return (root == null) || (isValidBST(root.left) && lastVal < (lastVal = root.val) && isValidBST(root.right));
}
}
//再写前面的两个递归方法
//递归,方法一:
public boolean isValidBST(TreeNode root){
return this.isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
public boolean isValidBST(TreeNode root, long min, long max){
if(root == null){
return true;
}else {
if(root.val <= min || root.val >= max){
return false;
}
boolean b1 = isValidBST(root.left, min, root.val);
boolean b2 = isValidBST(root.right, root.val, max);
return b1 && b2;
}
}
//递归,方法二:
public boolean isValidBST(TreeNode root){
return this.isValidBST(root, null, null);
}
public boolean isValidBST(TreeNode root, TreeNode tMin, TreeNode tMax){
if(root == null){
return true;
}else {
if(tMin != null && root.val <= tMin.val){
return false;
}
if(tMax != null && root.val >= tMax.val){
return false;
}
boolean b1 = isValidBST(root.left, tMin, root);
boolean b2 = isValidBST(root.right, root, tMax);
return b1 && b2;
}
}