【力扣一刷】代码随想录day20(654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树 )

目录

【654.最大二叉树】中等题

【617.合并二叉树】简单题

【700.二叉搜索树中的搜索】简单题

【98.验证二叉搜索树 】中等题


【654.最大二叉树】中等题

思路:关键 - > 单层递归逻辑

查找指定区间(左闭右开)内的最大值对应的索引,构造根节点,根据最大值索引划分左子树和右子树区间(左闭右开),构造根节点的左子树和右子树

相似题目:
【106.从中序与后序遍历序列构造二叉树】【105.从前序与中序遍历序列构造二叉树】

class Solution {

    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return getMaXTree(nums, 0, nums.length);
    }

    // 查找指定区间(左闭右开)内的最大值对应的索引
    public int findMaxIndex(int[] nums, int start, int end){
        int max = nums[start];
        int idx = start;
        for (int i = start; i < end; i++){
            if (nums[i] > max){
                max = nums[i];
                idx = i;
            }
        }
        return idx;
    }
    // 递归构建二叉树
    public TreeNode getMaXTree(int[] nums, int start, int end){
        if (start == end) return null;

        // 获取最大值索引
        int rootIdx = findMaxIndex(nums, start, end);
        // 构造根节点
        TreeNode root = new TreeNode(nums[rootIdx]);
        // 根据最大值索引划分左子树和右子树区间(左闭右开),构造左子树和右子树
        root.left = getMaXTree(nums, start, rootIdx);
        root.right = getMaXTree(nums, rootIdx + 1, end);
        return root;
    }
}


【617.合并二叉树】简单题

关键:确定终止条件

如果其中一个二叉树的根节点为null,则不需要覆盖,直接用另一个二叉树返回

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        // 确定终止条件:
        // 如果其中一个二叉树的根节点为null,则不需要覆盖,直接用另一个二叉树返回就行
        // 如果都是null,第一个if中root1 = null,会自动返回root2的null
        if (root1 == null) return root2;
        if (root2 == null) return root1;
        
        // 确定单层递归逻辑
        TreeNode root = new TreeNode();
        root.val = root1.val + root2.val; // 两个二叉树对应节点的值相加
        root.left = mergeTrees(root1.left, root2.left);  // 合并左子树
        root.right = mergeTrees(root1.right, root2.right); // 合并右子树
        return root;
    }
}


【700.二叉搜索树中的搜索】简单题

方法一  按照普通二叉树的性质搜索

思路:

  • 如果当前节点的val就是目标val,则返回当前节点
  • 否则,先搜索左子树,如果左子树中找到值等于目标val的节点,即leftRes不为null,则返回leftRes。
  • 如果左子树中找不到值等于val的节点,即leftRes=null,则返回右子树的搜索结果。
class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if (root == null) return null;

        // 如果当前节点的val就是目标val,则返回当前节点
        if (root.val == val) return root;
        // 否则,先搜索左子树,如果左子树中找到值等于val的节点,即leftRes不为null,则返回leftRes。
        TreeNode leftRes = searchBST(root.left, val);
        if (leftRes != null) return leftRes;
        // 如果左子树中找不到值等于val的节点,即leftRes=null,则返回右子树的搜索结果
        else return searchBST(root.right, val);
    }
}

方法二  按照二叉搜索树的性质搜索

【二叉搜索树】

二叉搜索树是一个有序树:(左节点 < 根节点 < 右节点)

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉搜索树

思路:

  • 如果当前节点的val就是目标val,则返回当前节点
  • 如果当前节点的val大于目标val,证明val只可能出现在当前节点的左子树中,返回左子树的搜索结果。
  • 如果当前节点的val小于目标val,证明val只可能出现在当前节点的右子树中,返回右子树的搜索结果。
class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        // 确定终止条件
        if (root == null) return null;

        // 如果当前节点的val就是目标val,则返回当前节点
        if (root.val == val)  return root;
        // 如果当前节点的val大于目标val,证明val只可能出现在当前节点的左子树中,返回左子树的搜索结果
        else if (root.val > val) return searchBST(root.left, val);
        // 如果当前节点的val小于目标val,证明val只可能出现在当前节点的右子树中,返回右子树的搜索结果
        else return searchBST(root.right, val);
        
    }
}


【98.验证二叉搜索树 】中等题

方法一  有效的二叉搜索树的中序遍历数组为升序数组(容易理解)

思路:

  • 递归,中序遍历,存放所有节点的val
  • 判断中序遍历数组是否是升序数组

注意:数组前后元素不能等于,要严格小于才是升序

class Solution {
    // 定义存放所有节点的val的数组
    List<Integer> nodes = new ArrayList<>();

    // 递归,中序遍历,存放所有节点的val
    public void inorder(TreeNode root){
        if (root == null) return;

        inorder(root.left);
        nodes.add(root.val);
        inorder(root.right); 
    }

    public boolean isValidBST(TreeNode root) {
        // 递归,中序遍历,存放所有节点的val
        inorder(root);

        // 判断数组是否保持升序,至少有一个节点,如果只有一个节点,则不进入for循环
        for (int i = 0; i < nodes.size() - 1; i++){
            if (nodes.get(i) >= nodes.get(i+1)) return false; // 注意:不能等于,要严格小于才是升序
        }
        return true;
    }
}

方法二   中序遍历二叉树过程中检测是否升序(难度较大)

粗暴版

思路:

1、先判断左子树是否为有效的二叉搜索树,是则判断当前节点是否大于左子树的所有节点的值。

2、如果当前节点是否大于左子树的所有节点的值,更新需要对比的pre值,并返回右子树是否为有效二叉树的判断结果,作为该树是否为有效二叉搜索树的结果。

3、如果上面步骤1的两个条件不符合,则不需要再遍历右子树,直接返回false即可。

class Solution {
    long pre = - (long) Math.pow(2, 31) - 1; // 节点值的最小值为-2147483648,即-2^31,pre的初始值要比这个最小值小
    public boolean isValidBST(TreeNode root) {
        if (root == null) return true;
        // 先判断左子树是否为有效的二叉搜索树,是则判断当前节点是否大于左子树的所有节点的值。
        if(isValidBST(root.left) && root.val > pre){
            // 如果当前节点是否大于左子树的所有节点的值,更新需要对比的pre值,并返回右子树是否为有效二叉树的判断结果
            pre = root.val;
            return isValidBST(root.right); 
        }
        // 如果上面两个条件不符合,则不需要再遍历右子树,直接返回false即可
        return false;
    }
}

优点:如果左子树已经不是有效的二叉搜索树,或者当前节点不是大于左子树的所有节点值,就不需要再遍历右子树,直接返回false。

优化版

思路:

如果节点的最小值 = long型的最小值,就不能像上面代码那样设置初始值。

矛盾在于pre需要初始化,才能与root.val比较,因此,可以使用TreeNode对象的val记录pre值,初始化为null,在比较前判断该TreeNode对象是否为null即可。

class Solution {
    TreeNode pre = null; // 节点值的最小值为-2147483648,即-2^31,pre的初始值要比这个最小值小
    public boolean isValidBST(TreeNode root) {
        if (root == null) return true;
        // 先判断左子树是否为有效的二叉搜索树,是则判断当前节点是否大于左子树的所有节点的值。
        if(isValidBST(root.left)){
            // 如果当前节点是否大于左子树的所有节点的值,更新需要对比的pre.val,并返回右子树是否为有效二叉树的判断结果
            if (pre == null || (pre != null && root.val > pre.val)){
                pre = root;
                return isValidBST(root.right); 
            }
        }
        // 如果上面两个条件不符合,则不需要再遍历右子树,直接返回false即可
        return false;
    }
}

【总结:所有的坑】

坑1:不能单纯的比较左节点小于中间节点,右节点大于中间节点,因为题目要求“左子树所有节点小于中间节点,右子树所有节点大于中间节点”。

坑2:最小值是-2^31,比较的初始值pre要设置比这个最小值小,但是int型变量的最小值也是-2^31,因此要设置为long型

方法三  统一迭代 - 中序遍历

思路:按照中序遍历,在处理节点时,判断节点的值是否比上一个访问的节点的值大,是则中序遍历数组为升序数组,否则返回false。

注意:判断条件的pre.val >= cur.val不要漏了等号,要严格小于才对。

class Solution {
    public boolean isValidBST(TreeNode root) {
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode cur = root; // 至少有一个节点,root不可能为null
        TreeNode pre = null;
        stack.push(cur);

        while (!stack.isEmpty()){
            cur = stack.pop();
            if (cur != null){
                // 按中序遍历的反序【右 - 中 - null - 左】入栈
                if (cur.right != null) stack.push(cur.right);
                stack.push(cur);
                stack.push(null);
                if (cur.left != null) stack.push(cur.left);
            }
            else{
                cur = stack.pop();
                // 如果前面的节点【大于或者等于】当前节点的值,证明不是升序数组,直接返回false
                if (pre != null && pre.val >= cur.val) return false;
                pre = cur;
            }
        }
        return true;
    }
}

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值