【算法训练营Day15&16】二叉树part5&6

最大二叉树

题目链接:654. 最大二叉树

解题逻辑:

这一题与106. 从中序与后序遍历序列构造二叉树很相似,都是通过切割数组完成二叉树的构建,但这题稍微简单一些。

我们可以通过先构建左右子树再拼接上根节点的方式来构建二叉树,所以我们选用后序遍历。接下来通过递归三部曲来分析:

  • 递归参数与返回值:我们需要接受递归的返回值用来构建左右子树,所以返回值定为TreeNode,而参数按照题意定位数组即可
  • 递归出口
    • 当数组为空的时候直接返回null
    • 当数组的长度为1的时候直接返回这个唯一值的节点即可
  • 递归单层逻辑
    • 通过for循环找到数组的最大值以及最大值的索引值
    • 根据最大值的索引对数组进行切分
    • 构造左、右节点
    • 根据最大值创建根节点然后拼接左右节点

代码如下:

class Solution {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        if(nums.length == 0) return null;
        else if(nums.length == 1) return new TreeNode(nums[0]);

        int devideIndex = 0;
        int max = 0;
        for(int i = 0;i < nums.length;i++) {
            if(nums[i] >= max) {
                devideIndex = i;
                max = nums[i];
            }
        }
        
        TreeNode left = constructMaximumBinaryTree(Arrays.copyOfRange(nums,0,devideIndex));
        
        TreeNode right;
        if(devideIndex + 1 > nums.length) right = null;
        else right = constructMaximumBinaryTree(Arrays.copyOfRange(nums,devideIndex + 1,nums.length));

        TreeNode root = new TreeNode(max);
        root.left = left;
        root.right = right;
        return root;
    }
}

合并二叉树

题目链接:617. 合并二叉树

解题逻辑:

本题的核心就是同时对两棵树进行递归遍历,在这里我们选用后序遍历,然后从递归三部曲的角度来分析:

  • 递归参数与返回值,参数因为是同时遍历两棵树所以要接收两个树节点,然后返回值就是TreeNode用来返回根节点
  • 递归出口:既然是两棵树同时遍历,再根据题目条件,也就是说两棵树的相同位置节点只要有一个不为null,那么就要遍历到,所以在这里我们的出口条件就是只有当两个节点均为null的时候才会推出递归。
  • 单层递归逻辑:
    • 递归获得左右节点
    • 根据传入的两个节点,如果有一个不为null,则使用该节点作为根节点
    • 如果都不为null,则将两节点的值相加作为新的根节点

代码如下:

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if(root1 == null && root2 == null) return null;

        TreeNode node;
        TreeNode left;
        TreeNode right;
        if (root1 == null && root2 != null) {
            node =  new TreeNode(root2.val);
            left = mergeTrees(null,root2.left);
            right = mergeTrees(null,root2.right);
        } else if(root1 != null && root2 == null) {
            left = mergeTrees(root1.left,null);
            right = mergeTrees(root1.right,null);
            node =  new TreeNode(root1.val);
        } else {
            left = mergeTrees(root1.left,root2.left);
            right = mergeTrees(root1.right,root2.right);
            node =  new TreeNode(root1.val + root2.val);
        }

        node.left = left;
        node.right = right;
        return node;
    }
}

二叉搜索树中的搜索

题目链接:700. 二叉搜索树中的搜索

解题逻辑:本题较为简单,直接比较节点值与搜索值,节点值比搜索值大,则往左递归查询,反之向右递归查询。

代码如下:

class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if(root == null) return null;
        
        if(root.val == val) return root;
        else if(root.val > val) {
            TreeNode left = searchBST(root.left,val);
            if (left != null) return left;
        }else {
            TreeNode right = searchBST(root.right,val);
            if (right != null) return right;
        }

        return null;
    }
}

验证二叉搜索树

题目链接:98.验证二叉搜索树

解题逻辑:

首先要明确二叉搜索树的概念是:根节点大于左子树,小于右子树

很容易错误理解为根节点大于左节点,小于右节点

他还有一个非常重要的特性就是其中序遍历是单调递增的,那么凭借这个特性我们就可以建立初步的思路。

我们再进行中序遍历的时候,将当前节点的前一个相邻节点暂存在类字段中,这样我们在遍历到一个节点的时候,就与类字段中的值进行比对,如果当前节点的值比类字段中的值要小就说明不满足二叉搜索树。

后文中若出现上一个节点,均代表二叉搜索树中序遍历的上一个节点。

代码如下:

class Solution {

    long value = Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
        if(root == null) return true;

        boolean left = isValidBST(root.left);
        if(left == false) return false;

        if(root.val <= value) return false;
        else value =  root.val;

        boolean right = isValidBST(root.right);
        if(right == false) return false;

        return true;
    }
}

二叉搜索树的最小绝对差

题目链接:530. 二叉搜索树的最小绝对差

解题逻辑:

根据二叉搜索树的性质,我们可以推断出二叉搜索树的最小绝对差存在于中序遍历的相邻元素之间。我们仍使用上一题的思路:将前一个节点的值暂存到类字段中。同时将最小值也存到类字段中。

这样每当遍历到一个新节点,我们只需要将当前节点的值与类字段中的值做差取绝对值再与类字段中的最小值作比较即可。

class Solution {

    int item = Integer.MAX_VALUE;
    int min = Integer.MAX_VALUE;
    public int getMinimumDifference(TreeNode root) {
        inRead(root);
        return min;
    }
    
    public void inRead(TreeNode node){
        if(node == null) return;
        inRead(node.left);
        int result = Math.abs(node.val - item);
        if(result < min) min = result;
        item = node.val;
        inRead(node.right);

    }
}

二叉搜索树中的众数

题目链接:501.二叉搜索树中的众数

该题同样也是使用到了暂存的思想,我们设立了四个类字段:

  • item: 用来暂存上一个节点的值
  • curCount: 用来暂存节点的出现次数
  • maxCount: 用来暂存节点出现的最大次数
  • result :用来暂存众数

解题逻辑如下:

使用中序遍历遍历整个二叉搜索树。将当前元素与类字段中的上一个元素item作比较:

  • 如果相同,则将curCount + 1
  • 如果不同,则需要对curCount、maxCount、result 三个字段进行修改。这个修改的逻辑就是如果统计的当前次数与最大次数一样那么直接添加到结果集中,而如果当前次数比最大次数还要大,那么就要更新最大次数,然后清空结果集,再将上一个节点item加入。最后要将curCount置1,然后将item更新成当前节点,再进入到下一层递归

最最需要注意的就是,当我们对整颗二叉搜索树完成了中序遍历之后,如果最后一个节点与上一个节点值相同走的是curCount + 1的逻辑,那么这个时候的result还需要再更新一次!

代码如下:

class Solution {
    int item = Integer.MAX_VALUE;
    int curCount = 0;
    int maxCount = -1;
    List<Integer> result = new ArrayList<>();
    public int[] findMode(TreeNode root) {
        inRead(root);
        changeResult();
        return result.stream().mapToInt(Integer::intValue).toArray();
    }

    public void inRead(TreeNode node){
        if(node == null) return;
        
        inRead(node.left);

        if(node.val == item) {
            curCount++;
        }else {
            changeResult();
            curCount = 1;
            item = node.val;
        }      
          
        inRead(node.right);
    }

    public void changeResult(){
        if(curCount > maxCount) {
            maxCount = curCount;
            result.clear();
            result.add(item);
        }else if(curCount == maxCount) {
            result.add(item);
        }
    }
}

总结:二叉搜索树的常规思路

本篇文章中的二叉搜索树相关的题目,经常用到的一个重要的特性就是:二叉搜索树的中序遍历是单调递增的

同时还有一个暂存的思想也经常用到,其具体操作就是在对二叉搜索树进行中序遍历的时候使用类字段对前一个元素进行暂存,类似于一个寄存器的作用,这样就可以将二叉搜索树中的两个相邻节点进行关联操作。这种暂存的效果在代码上的实现:只需要将类变量的赋值操作放在当前节点的操作逻辑之后就可。也就是说在递归到一个新节点之后,只要不立刻对类字段重新赋值,那么他就是前一个节点的数值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十八岁讨厌编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值