leetCode - - - 二叉树

news2024/9/20 5:44:50

目录​​​​​​​

1.前中后序遍历(递归)

2.前中后序遍历(迭代)

3.翻转二叉树(LeetCode 226)

4.最大二叉树( LeetCode 654 )

5.平衡二叉树( LeetCode 110 )

6.二叉树的最小深度( LeetCode 111 )

7.二叉树的最大深度( LeetCode 104 )

8.二叉树的最近公共祖先( LeetCode 236 )(重点)

9.从前序与中序遍历序列构造二叉树( LeetCode 105 )(重点)

10.从中序与后序遍历序列构造二叉树( LeetCode 106 )

11.合并二叉树(LeetCode 617)

12.验证二叉搜索树(LeedCode 98)


1.前中后序遍历(递归)

前序

https://leetcode.cn/problems/binary-tree-preorder-traversal/

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list=new ArrayList<>();
        preorder(root,list);
        return list;
    }
    private void preorder(TreeNode root,List<Integer>list){
        if(root==null){
            return;
        }
        list.add(root.val);
        preorder(root.left,list);
        preorder(root.right,list);
    }
}

中序

 https://leetcode.cn/problems/binary-tree-inorder-traversal/

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list=new ArrayList();
        inorder(root,list);
        return list;
    }
    private void inorder(TreeNode root,List<Integer> list){
        if(root==null){
            return;
        }
        inorder(root.left,list);
        list.add(root.val);
        inorder(root.right,list);
    }
}

后序

https://leetcode.cn/problems/binary-tree-postorder-traversal/

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list=new ArrayList();
        postorder(root,list);
        return list;
    }
    private void postorder(TreeNode root,List<Integer> list){
        if(root==null){
            return;
        }
        postorder(root.left,list);
        postorder(root.right,list);
        list.add(root.val);
    }
}

2.前中后序遍历(迭代)

先整体写出遍历的顺序,然后再根据前中后序的顺序,确定添加到List的时机。三种遍历统一可以使用一个模版

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        // 设置一个数组用来保存二叉树前序遍历的结果
        List<Integer> preorderResult=new ArrayList<>();

        // 设置一个数组用来保存二叉树中序遍历的结果
        //List<Integer> inorderResult = new ArrayList<>();
        // 设置一个数组用来保存二叉树后序遍历的结果
        //List<Integer> postorderResult = new ArrayList<>();

         // 设置一个栈,用来保存路径
        Stack<TreeNode> stack=new Stack<>();
         // 左代表该节点的左右孩子节点都没有遍历
        int nodeLeft=1;
         // 右代表该节点的左孩子节点已经遍历,右孩子节点还没有遍历
        int nodeRight=2;
         // 上代表左右孩子节点都已经遍历,需要返回到它的父节点
        int nodeUp=3;
        // 每个节点的初始化状态都是从左开始
        int nodeState=nodeLeft;
        TreeNode node=root;

        while(node!=null){
           
            if(nodeState==nodeLeft){
                preorderResult.add(node.val);
                if(node.left!=null){
                    stack.push(node);
                    node=node.left;
                }else{
                    nodeState=nodeRight;
                }
            }else if(nodeState==nodeRight){

               // 把当前节点加入到二叉树中序遍历的结果数组中
               // inorderResult.add(node.val);

                if(node.right!=null){
                    stack.push(node);
                    node=node.right;
                    nodeState=nodeLeft;
                }else{
                    nodeState=nodeUp;
                }
            }else if(nodeState==nodeUp){
               
               // 把当前节点加入到二叉树后序遍历的结果数组中
               //postorderResult.add(node.val);

                TreeNode parent=null;
                if(!stack.isEmpty()){
                    parent=stack.pop();

                    if(parent.left==node){
                    nodeState=nodeRight;
                }
                }
                node=parent;
            }
        }
        return preorderResult;
     }
}

3.翻转二叉树(LeetCode 226)

https://leetcode.cn/problems/invert-binary-tree/description/

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

示例 1:

输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root==null){
            return null;
        }
        if(root.left==null && root.right==null){
            return root;
        }
        TreeNode left=invertTree(root.left);
        TreeNode right=invertTree(root.right);

        root.left=right;
        root.right=left;
        return root;
    }
}

class Solution {
    public TreeNode invertTree(TreeNode root) {

        // 1、递归终止条件一,当前节点为空的时候,就返回 null
        if(root == null) return null;

        // 2、递归终止条件二,当前节点为叶子节点的时候,就返回这个节点
        if( root.left == null && root.right == null){
            
            // 返回这个节点
            return root;

        }

        // 3、把当前这个节点 root 的左子树进行翻转操作
        TreeNode left =  invertTree(root.left);

        // 4、把当前这个节点 root 的右子树进行翻转操作
        TreeNode right = invertTree(root.right);

        // 5、把翻转成功的右子树赋值给 root 的左子树
        root.left = right;

        // 6、把翻转成功的左子树赋值给 root 的右子树
        root.right = left;

        // 7、返回翻转成功的二叉树的根节点
        return root;

    }
}

4.最大二叉树( LeetCode 654 )

https://leetcode.cn/problems/maximum-binary-tree/description/

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:

  1. 创建一个根节点,其值为 nums 中的最大值。
  2. 递归地在最大值 左边 的 子数组前缀上 构建左子树。
  3. 递归地在最大值 右边 的 子数组后缀上 构建右子树。

返回 nums 构建的 最大二叉树 

class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
       return maxBinaryTreeNode(nums,0,nums.length-1);
    }

    private TreeNode maxBinaryTreeNode(int[]nums,int left,int right){
        if(left>right){
            return null;
        }
        if(left==right){
            return new TreeNode(nums[left]);
        }
        int maxIndex=left;
        for(int i=left+1;i<right+1;i++){
            if(nums[i]>nums[maxIndex]){
                maxIndex=i;
            }
        }
        TreeNode root=new TreeNode(nums[maxIndex]);
        TreeNode leftNode=maxBinaryTreeNode(nums,left,maxIndex-1);
        TreeNode rightNode=maxBinaryTreeNode(nums,maxIndex+1,right);
        root.left=leftNode;
        root.right=rightNode;
        return root;
    }
}

5.平衡二叉树( LeetCode 110 )

https://leetcode.cn/problems/balanced-binary-tree/

给定一个二叉树,判断它是否是 

平衡二叉树:每个节点 的左右两个子树的高度差的绝对值不超过 1 。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:true

示例 2:

输入:root = [1,2,2,3,3,null,null,4,4]
输出:false

class Solution {
    public boolean isBalanced(TreeNode root) {
        return recur(root)!=-1;
    }
    private int recur(TreeNode curNode){
        if(curNode==null){
            return 0;
        }
        int left=recur(curNode.left);
        if(left==-1) return -1;
        int right=recur(curNode.right);
        if(right==-1) return -1;

        return Math.abs(left-right)<2 ? Math.max(left,right)+1:-1;
    }
}
class Solution {
    public boolean isBalanced(TreeNode root) {
         // 自底向上判断,只要返现有叶子树出现非平衡二叉树的情况,那么就是不是平衡二叉树了
        return recur(root) != -1;
    }

    private int recur(TreeNode curNode) {
        
        // 递归终止条件
        // 如果当前节点 curNode 为空,那么高度就是 0
        if (curNode == null) return 0;

        // 递归的计算当前节点 curNode 的左子树高度
        int left = recur(curNode.left);

        // 如果发现为 -1,即表示左子树中出现了高度差大于 2 的情况,已经不是平衡二叉树了,可以直接返回这个结果给上层
        // 提前终止了后续的判断
        if(left == -1) return -1;

        // 递归的计算当前节点 curNode 的右子树高度
        int right = recur(curNode.right);

        // 如果发现为 -1,即表示右子树中出现了高度差大于 2 的情况,已经不是平衡二叉树了,可以直接返回这个结果给上层
        // 提前终止了后续的判断
        if(right == -1) return -1;

        // 1、否则说明在 curNode 的左子树中没有发现不是平衡二叉树的情况
        // 2、否则说明在 curNode 的右子树中没有发现不是平衡二叉树的情况
        // 因此计算一下当前节点 curNode 是否是平衡二叉树
        // 即计算 curNode 的左子树高度与右子树高度差
        // 如果发现小于 2,那么返回以当前节点 curNode 为根节点的子树的最大高度
        // 即节点 curNode 的左右子树中最大高度加 1 
        return Math.abs(left - right) < 2 ? Math.max(left, right) + 1 : -1;
    }
}

6.二叉树的最小深度( LeetCode 111 )

即为求最短路径

https://leetcode.cn/problems/minimum-depth-of-binary-tree/

class Solution {
    public int minDepth(TreeNode root) {
        Queue<TreeNode> nodeTree=new LinkedList<>();
        if(root==null){
            return 0;
        }
        nodeTree.add(root);
        int depth=0;
        while(nodeTree!=null){
            int size=nodeTree.size();
            depth++;
            for(int i=0;i<size;i++){
                TreeNode node=nodeTree.poll();
                if(node.left==null && node.right==null){
                    return depth;
                }
                if(node.left!=null){
                    nodeTree.add(node.left);
                }
                if(node.right!=null){
                    nodeTree.add(node.right);
                }
            }
        }
        return depth;
    }
}

来一个趴菜现在才知道的知识点。。。虽然此题判断条件中queue != null 和 !queue.isEmpty() 都不会报错。。。

!queue.isEmpty()更准确,queue != null不报错是因为上面有添加root,对queue已经有初始化了。

queue != null 和 !queue.isEmpty() 是两个用于不同目的的检查,它们在代码中的使用情况和意图有所不同。
queue != null 是用来确认队列对象是否已经初始化。它主要用于确保代码在访问队列之前不会引发 NullPointerException。
!queue.isEmpty() 是用来检查队列是否有元素。在尝试访问或操作队列的元素之前进行检查,以确保队列中有数据可以处理。
通常,这两个检查是相辅相成的。在处理队列时,首先需要确认队列对象本身是有效的 (queue != null),然后检查队列是否包含元素 (!queue.isEmpty())。

class Solution {
    public int minDepth(TreeNode root) {
        // 边界情况处理
        if (root == null) return 0;

        // 设置一个队列,用来存储二叉树中的元素
        Queue<TreeNode> nodeQueue = new LinkedList<>();

        // 队列添加二叉树的根节点
        nodeQueue.add(root);

        // 设置 depth 用来保存输出结果
        int depth = 0;

        // 遍历队列,直到队列为空,说明访问了二叉树中所有的节点
        while (!nodeQueue.isEmpty()) {

            // 用来记录 queue 的长度,即每层节点的个数
            int size = nodeQueue.size();

            // 每到一层,深度就 +1
            depth++;

            // 使用 for 循环,将 nodeQueue 中的元素统计
            for (int i = 0; i < size; i++) {

                // 从 queue 中取出一个节点,此时,nodeQueue 已经抛出了这个节点      
                TreeNode curNode = nodeQueue.poll();

                // curNode.left == null && curNode.right == null 
                // 说明是叶子结点
                // 由于【最小深度是从根节点到最近叶子节点的最短路径上的节点数量】
                // 直接返回 depth 
                if(curNode.left == null && curNode.right == null){
                    return depth;
                }

                // 判断当前节点的左子节点是否有值,如果有,则添加到 nodeQueue 中
                if (curNode.left != null){
                    nodeQueue.add(curNode.left);
                } 

                // 判断当前节点的右子节点是否有值,如果有,则添加到 nodeQueue 中    
                if (curNode.right != null){
                    nodeQueue.add(curNode.right);
                }

            }
        }

        // 返回 depth
        return depth;
    }
}

7.二叉树的最大深度( LeetCode 104 )

https://leetcode.cn/problems/maximum-depth-of-binary-tree/description/

给定一个二叉树 root ,返回其最大深度。

二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。

class Solution {
    public int maxDepth(TreeNode root) {

        // 如果 root 为空,直接返回 0
        if(root == null) return 0;

        // 递归调用 maxDepth,求出当前节点的左子树的最大深度
        int left = maxDepth(root.left);

        // 递归调用 maxDepth,求出当前节点的右子树的最大深度
        int right = maxDepth(root.right);

        // 求出当前节点的左右子树中较大的值
        int childMaxDepth = Math.max(left,right);

        // 二叉树的最大深度就是它的左右子树中较大的值加上 1
        return childMaxDepth + 1;
    }
}

8.二叉树的最近公共祖先( LeetCode 236 )(重点)

https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null) return null;
        if(root==p) return p;
        if(root==q)  return q;
        TreeNode left=lowestCommonAncestor(root.left,p,q);
        TreeNode right=lowestCommonAncestor(root.right,p,q);
        if(left==null && right==null){
            return null;
        }else if(left==null){
            return right;
        }else if(right==null){
            return left;
        }else{
            return root;
        }
    }
}
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {

        // 1、如果此时访问的节点 root 为 null,那么直接返回 null
        if(root == null ) return null;

        // 2、如果此时访问的节点 root 为指定节点 p,那么返回 p 这个节点
        if(root == p)  return p;

        // 3、如果此时访问的节点 root 为指定节点 q,那么返回 q 这个节点
        if(root == q) return q;
        
        // 4、经过上面 1、2、3 的判断之后,root 这个节点必然不是 p、q、null 这三种情况的节点
        // 接下来,去递归的判断当前节点 root 的左右子树是否包含 p 、q 这两个指定节点


        // 5、递归的去查找 root 的左子树,判断是否包含p 、q 这两个指定节点
        // 如果 root 的左子树节点和它的左子树所有节点中包含 p,那么 left 的值就是 p
        // 如果 root 的左子树节点和它的左子树所有节点中包含 q,那么 left 的值就是 q
        // 如果 root 的左子树节点和它的左子树所有节点中既不包含 p,也不包含 q,那么 left 的值就是 null
        TreeNode left = lowestCommonAncestor(root.left, p, q);


        // 6、递归的去查找 root 的右子树,判断是否包含p 、q 这两个指定节点
        // 如果 root 的右子树节点和它的右子树所有节点中包含 p,那么 right 的值就是 p
        // 如果 root 的右子树节点和它的右子树所有节点中包含 q,那么 right 的值就是 q
        // 如果 root 的右子树节点和它的右子树所有节点中既不包含 p,也不包含 q,那么 right 的值就是 null
        TreeNode right = lowestCommonAncestor(root.right, p, q);


        // 7、判断完当前节点 root 、 root 的左子树 left、root 的右子树 right 的情况后
        // 开始向父节点传递信息

        // 8、如果 root 节点的左子树 left 和右子树 right 都没有找到指定节点 p、q
        // 并且 root 自己本身也不是指定节点 p、q
        // 那么它给父节点传递的信息就是以 root 为根节点的那颗二叉树没有指定节点 p、q 
        if(left == null && right == null){

            // 返回 null,告诉 root 的父节点,这里没找到指定节点 p、q
            return null;

        // 9、如果在 root 节点的左子树 left 中没有找到指定节点 p、q 
        // 那么以 root 为根节点的那颗二叉树能不能找到指定节点 p、q  完全取决于 right 了
        }else if(left == null){

            // 返回 right ,告诉 root 的父节点,能不能找到指定节点 p、q  完全取决于 right 
            return right;

        // 10、如果在 root 节点的右子树 right 中没有找到指定节点 p、q 
        // 那么以 root 为根节点的那颗二叉树能不能找到指定节点 p、q  完全取决于 left 了
        }else if(right == null){

            // 返回 left ,告诉 root 的父节点,能不能找到指定节点 p、q  完全取决于 left 
            return left;

        // 11、此时,left != null 并且 right != null
        // 这说明在以 root 节点为根节点的那棵二叉树中找到了指定节点 p ,也找到了指定节点 q 
        // 那么就告诉父节点,root 就是 p、q 的最近公共祖先
        }else{

            // 返回 root ,告诉 root 的父节点,root 就是 p、q 的最近公共祖先
            return root;
        } 
    }
}

9.从前序与中序遍历序列构造二叉树( LeetCode 105 )(重点)

https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        HashMap<Integer,Integer> map=new HashMap<>();
        for(int i=0;i<inorder.length;i++){
            map.put(inorder[i],i);
        }
        TreeNode root=new TreeNode(preorder[0]);
        for(int i=1;i<preorder.length;i++){
            TreeNode node=new TreeNode(preorder[i]);
            insert(root,node,map);
        }
        return root;
    }

    private void insert(TreeNode root,TreeNode node,HashMap<Integer,Integer> map){
        while(node!=root){
            if(map.get(node.val)<map.get(root.val)){ 
                if(root.left==null){
                    root.left=node;
                }
                root=root.left;
            }else{
                if(root.right==null){
                    root.right=node;
                }
                root=root.right;
            }
        }
    }
}
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {

        // 题目中说 preorder 和 inorder 均无重复元素
        // 通过哈希表把中序遍历序列中的值和顺序建立映射关系
        HashMap<Integer, Integer> map = new HashMap<>();

        // 通过 for 循环,遍历完中序遍历序列中的所有元素
        for (int i = 0; i < inorder.length; i++) {
             // 以中序序列中的元素值 inorder[i] 作为 key
             // 以位置 i 作为 value
             // 存放到哈希表中
             // 比如中序遍历序列中的元素是 [   A  ,   D  ,   E  ,   F  ,   G  ,   H  ,   M  ,   Z  ]
             // 那么这个哈希表就是以下的样子
             // | 索引 | 位置  |
             // | A | 0  |
             // | D | 1  |
             // | E | 2  | 
             // | F | 3  | 
             // | G | 4  | 
             // | H | 5  | 
             // | M | 6  | 
             // | Z | 7  |            
             map.put(inorder[i], i);
        }

        // 下面开始构建二叉树
        // 把前序遍历序列中的第一个元素 preorder[0] 作为二叉树的根节点
        // 因为任意二叉树的前序遍历序列中的第一个元素,一定是二叉树的根节点
        TreeNode root = new TreeNode(preorder[0]);


        // 继续遍历前序遍历序列中的其它元素
        for(int i = 1 ; i < preorder.length ; i++){

            // 把当前遍历的元素构造为一个二叉树的节点
            TreeNode node = new TreeNode(preorder[i]);

            // 把构造的节点插入到以 root 为根节点的二叉树中
            insertNode(root,node,map);

        }

        // 当 preorder 中所有元素都构造并且插入完毕之后
        // 二叉树就完成了构建
        return root;


    }

    // root : 二叉树的根节点
    // node : 待插入的节点
    // map : 节点值和中序遍历序列位置的映射
    private void insertNode(TreeNode root,TreeNode node,HashMap<Integer,Integer> map){

            // 当 root 和 node 指向的节点相同时,跳出循环
            while(root != node){

                // 如果 node 的中序遍历序列位置小于 root 的中序遍历序列位置
                // 说明 node 应该在 root 的左子树中
                if(map.get(node.val) < map.get(root.val)){

                    // 如果此时 root 没有左子树
                    if(root.left == null){
                        // 那么就把 node 设置为 root 的左子树
                        root.left = node;
                    }

                    // 1、如果之前 root 没有左子树,那么通过上面的代码,就设置好了 root 的左子树
                    // 也就是说 node 是 root 的一个叶子节点,完成了插入操作
                    // 把 root 指向 root.left 后,root 为 node,跳出了循环

                    // 2、如果之前 root 已经有左子树,那么就不能直接把 node 插入到 root 的左子树上
                    // 需要判断应该把 node 插入到 root 左子树上的孩子节点的那个位置上
                    // 比如现在的 root 是这个样子,node 为 A
                    //        G
                    //       /
                    //      D
                    //     /  \
                    //    ①   ②
                    // root 已经有左子树 D 了,所以 node 应该考虑插入到 D 中的 ① 还是 ② 上面
                    // 所以,把 root 指向 root.left ,继续遍历 root 的左子树的情况
                    root = root.left;

                    
                }else{

                    // 如果 node 的中序遍历序列位置大于 root 的中序遍历序列位置
                    // 说明 node 应该在 root 的右子树中
                    
                    // 如果此时 root 没有右子树
                    if(root.right == null){
                        // 那么就把 node 设置为 root 的右子树
                        root.right = node;
                    }

                    // 把 root 指向 root.right ,继续遍历 root 的右子树的情况
                    root = root.right;
                    
                }

            }
    }
}

10.从中序与后序遍历序列构造二叉树( LeetCode 106 )

https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/

与上面前中构造基本一致,不同的是后序数组从后往前遍历,后序数组的最后一个值,为整棵树的根节点。

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        HashMap<Integer,Integer> map=new HashMap<>();
        for(int i=0;i<inorder.length;i++){
            map.put(inorder[i],i);
        }
        TreeNode root=new TreeNode(postorder[postorder.length-1]);
        for(int i=postorder.length-2;i>=0;i--){
            TreeNode node=new TreeNode(postorder[i]);
            insert(root,node,map);
        }
        return root;
    }

    private void insert(TreeNode root,TreeNode node,HashMap<Integer,Integer> map){
        while(node!=root){
            if(map.get(node.val)<map.get(root.val)){ 
                if(root.left==null){
                    root.left=node;
                }
                root=root.left;
            }else{
                if(root.right==null){
                    root.right=node;
                }
                root=root.right;
            }
        }
    }
}

11.合并二叉树(LeetCode 617)

https://leetcode.cn/problems/merge-two-binary-trees/description/​​​​​​​

给你两棵二叉树: root1 和 root2 。

想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。

返回合并后的二叉树。

注意: 合并过程必须从两个树的根节点开始。

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
           if(root1==null && root2==null){
            return null;
           }
           if(root1==null){
            return root2;
           }
           if(root2==null){
            return root1;
           }
           TreeNode mergeNode=new TreeNode(root1.val+root2.val);
           mergeNode.left=mergeTrees(root1.left,root2.left);
           mergeNode.right=mergeTrees(root1.right,root2.right);
           return mergeNode;
    }
}

12.验证二叉搜索树(LeedCode 98)

https://leetcode.cn/problems/validate-binary-search-tree/

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

解法一:中序遍历+比较数组是否单调递增 

//二叉搜索树的中序遍历,节点的值一定是单调递增的
 //将二叉搜索树的中序遍历的值存放在数组中
 //判断这个数组是否是单调递增的
class Solution {
    public boolean isValidBST(TreeNode root) {
        List<Integer> list=new ArrayList<>();
        inorderTree(root,list);
        return isCreasing(list);
    }

    private void inorderTree(TreeNode root,List<Integer> list){
        if(root==null){
            return;
        }
        inorderTree(root.left,list);
        list.add(root.val);
        inorderTree(root.right,list);
    }

    private boolean isCreasing(List<Integer> list){
        for(int i=1;i<list.size();i++){
            if(list.get(i)<=list.get(i-1)){
                return false;
            }
        }
        return true;
    }
}

解法二:中序遍历+双指针

相比上种方法,少了一步存放各个节点的值。在中序遍历的过程中,每次比较当前应该节点和当前节点的大小即可。

class Solution {
    public boolean isValidBST(TreeNode root) {
        return inorderTree(root);
   }
   //如果当前节点为空,则返回 true。这是递归的终止条件,也表明空节点是有效的 BST。
   TreeNode pre=null;

   private boolean inorderTree(TreeNode root){
    if(root==null){
        return true;
    }
    //首先递归遍历当前节点的左子树。如果左子树不符合 BST 的条件(返回 false),则当前树也不符合 BST 的条件,返回 false。
    if(!inorderTree(root.left)){
        return false;
    }
    //prev != null:确保 prev 已经被初始化,检查当前节点值 node.val 是否大于前一个节点的值prev.val
    if(pre!=null && root.val<=pre.val){
        return false;
    }

    //更新 prev 为当前节点,以便在接下来的遍历中进行比较
    pre=root;
    //遍历当前节点的右子树。如果右子树不符合 BST 的条件(返回 false),则返回 false;否则,返回 true
    return inorderTree(root.right);
   }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2076935.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

02- javascript 高阶-构造函数(知识点)呀

目录 1.构造函数 1.1 JS构造函数的实例成员和静态成员 1.1.1实例成员 1.1.2静态成员 1.2构造函数原型prototype 1.3对象原型 1.4 constructor构造函数 1.5原型链 1.6构造函数实例和原型对象三角关系 1.7原型链和成员的查找机制 1.7.1 Object.prototype.hasOwnPropert…

WEB渗透Win提权篇-提权工具合集

提权工具合集包&#xff1a; 夸克网盘分享 往期文章 WEB渗透Win提权篇-RDP&Firewall-CSDN博客 WEB渗透Win提权篇-MSSQL-CSDN博客 WEB渗透Win提权篇-MYSQL-udf-CSDN博客 WEB渗透Win提权篇-AccountSpoofing-CSDN博客 WEB渗透Win提权篇-弱权限提权-CSDN博客 Tools合集 工…

UVa1670/LA5920 Kingdom Roadmap

UVa1670/LA5920 Kingdom Roadmap 题目链接题意分析AC 代码 题目链接 本题是2011年icpc欧洲区域赛东北欧赛区的K题 题意 输入一个n&#xff08;n≤100000&#xff09;个结点的树&#xff0c;添加尽量少的边&#xff0c;使得任意删除一条边之后图仍然连通。如下图所示&#xff0…

如果是你,你会背叛师门吗?看了凌晨一点杭州隧道里睡满的外卖员,我觉得李佳琦被骂一点也不冤——早读(逆天打工人爬取热门微信文章解读)

如果是你&#xff0c;你会背叛师门吗&#xff1f;&#xff1f; 引言Python 代码第一篇 洞见 看了凌晨一点杭州隧道里睡满的外卖员&#xff0c;我觉得李佳琦被骂一点也不冤第二篇 股市风云结尾 (先不论人品如何&#xff0c;这个问题就有点类似董宇辉跟新东方&#xff0c;大伙且看…

Java学习第一天

Java介绍&#xff1a; Java是一种高级编程语言&#xff1b;由sun公司研发&#xff0c;之后被Oracle公司收购&#xff1b;Java之父是詹姆斯.高斯林&#xff1b;Java什么都能做但主要用于企业互联网系统的开发&#xff1b;包括JavaSE&#xff08;标准版&#xff09;、JavaEE&…

C程序设计——表达式的值0

表达式 表达式是一组序列&#xff0c;由操作符或操作数组成。 表达式的值 C语言中&#xff0c;所有的表达式&#xff0c;都是有值的&#xff0c;所以本节专门讲讲表达式的值。 算数表达式 算数表达式的值&#xff0c;就是算数运算的结果&#xff0c;比如表达式 1 1的值就…

Threejs三要素及demo

本文目录 前言一、threejs三要素1.1 场景Scene1.2 相机Camera1.3 渲染器Renderer1.4 项目构建准备 二、安装Threejs2.1 编写代码 前言 Three.js是一个在 MIT 许可下的 JavaScript 库&#xff0c;它在 WebGL 之上运行。这个库的目标就是简化处理3D内容的过程。它是一个WebGL引擎…

【Linux】第一次使用linux向gitee上提交代码

1.首先要在gitee上新建一个仓库 2.然后&#xff0c;复制https的仓库链接 3. 三板斧 第一斧 git add . 4.三板斧 第二斧 git commit -m ‘日志’ 5.三板斧 第三斧 git push

QQ官方BOT 机器人Python实现群聊本地图片【base64编码】上传及其发送

参考&#xff1a;实现群聊本地图片【base64编码】上传及其发送 by SlieFamily 拉取请求 #199 腾讯连接/Botpy (github.com) 首先找到api.py&#xff0c;如果你是通过pip安装的botpy。找到包对应的地址&#xff0c;如果是clone的&#xff0c;也直接找到api.py。 打开后&#xff…

io进程----文件io

目录 一丶概念 二丶特点 三丶函数 1.打开文件 open 2.关闭文件 close 3.读取文件 read 4.写入文件 write 5.文件定位操作 标准IO和文件IO区别 四丶获取文件属性 1.stat函数 2.获取文件类型 五丶目录操作 一丶概念 在posix(可移植操作系统接口)中定义的一组输入输出…

STC89C52 定时器浅谈

文章目录 1、定时器1.1 定时器简介1.2 定时器构成1.2.1 系统时钟1.2.2 计数单元1.2.3 中断系统 1.2 定时器0/1的相关寄存器1.2.1 TMOD1.2.2 TCON 1.3 初始化定时器0 1、定时器 1.1 定时器简介 定时器&#xff0c;又称为计数器&#xff0c;是51单片机的内部资源&#xff0c;即…

16、CPU缓存架构

CPU缓存架构 CPU缓存架构详解CPU高速缓存概念CPU多核缓存架构CPU多核缓存架构缓存一致性问题分析 CPU缓存架构缓存一致性的解决方案缓存一致性协议实现原理总线窥探工作原理窥探协议类型 缓存一致性协议MESI协议 伪共享的问题linux下查看Cache Line大小避免伪共享方案 CPU缓存架…

netty编程之实现HTTP服务

写在前面 http是应用层协议&#xff0c;是我们日常开发中直接用到最多的协议了。本文来看下通过netty如何实现。 1&#xff1a;程序 netty不仅仅提供了String相关的编解码器&#xff0c;还贴心的提供了http相关的编码器和解码器&#xff0c;直接拿来用就行了&#xff0c;所以…

【3.3】贪心算法-解分发糖果

一、题目 老师想给孩子们分发糖果&#xff0c;有N 个孩子站成了一条直线&#xff0c;老师会根据每个孩子的表现&#xff0c;预先给他们评分。 你需要按照以下要求&#xff0c;帮助老师给这些孩子分发糖果&#xff1a; 1&#xff09;每个孩子至少分配到1 个糖果。 2&#xff…

apache经典模型和nginx参数配置

Apache 几个经典的 Web 服务端 ①Apache prefork 模型 ● 预派生模式&#xff0c;有一个主控制进程&#xff0c;然后生成多个子进程&#xff0c;使用select模型&#xff0c;最大并发 1024 ● 每个子进程有一个独立的线程响应用户请求 ● 相对比较占用内存&#xf…

Cyberchef实用功能之-模拟沙箱的文件静态分析能力

本文将介绍如何使用cyberchef 提取文件的重要元数据的信息&#xff0c;根据自己的需求实现沙箱中的静态文件的分析能力。 在网络安全日常的运营&#xff0c;护网行动&#xff0c;重保活动的过程中&#xff0c;样本的分析是一个重要的过程&#xff0c;这些可疑或者恶意的样本的…

Beyond Compare忽略特定格式文本

1 问题背景&#xff1a; 文本对比时忽略某些文本。比如有些生成的文件需要做差异对比&#xff0c;除了内容有差异外&#xff0c;自动生成的ID也不同&#xff0c;想忽略这些ID。特别是文件内容比较多的时候。 如上图&#xff0c;其中UUID“*”的部分我想忽略。 方法&#xff1…

微信开放平台应用签名MD5

可以使用JS转换一下 一、在任一网页 右键 检查 打开调试 二、把字母转换成小写&#xff0c;去除&#xff08;:&#xff09; MD5 应用签名示例 "70:71:5F:CA:AE:E5:B1:01:30:11:8F".toLowerCase().replace(/:/g, "") 70715fcaaee5b10130118f 就是要填写…

我们为什么推出数据模型

调用接口之前&#xff0c;要写大量的字段校验代码&#xff1f; 需要关联读写多张表的数据&#xff0c;难以写出复杂的数据库语法&#xff1f; 不仅需要开发核心的业务逻辑&#xff0c;还需要加班搭建CMS和数据管理页面&#xff1f; 数据不止放在云开发&#xff0c;还需要查询…

ctfhub-web-基础认证

HTTP协议&#xff1a;什么是HTTP基本认证_网易订阅 https://zhuanlan.zhihu.com/p/64584734 弹框输入密码账号bp抓包&#xff0c;发现了Authorization&#xff1a;后面有一串BASE64的编码(输入的密码) 账号默认是admin&#xff0c;抓包后是basic 把抓到的包发送到爆破模块i…