【代码随想录训练营】【Day19休息】【Day20】第六章|二叉树|654.最大二叉树|617.合并二叉树|700.二叉搜索树中的搜索|98.验证二叉搜索树

news2024/9/20 0:52:54

最大二叉树

题目详细:LeetCode.654

这道题在题目几乎就说明了解题的思路了:

  • 创建一个根节点,其值为 nums 中的最大值;
  • 递归地在最大值左边的子数组上构建左子树;
  • 递归地在最大值右边的子数组上构建右子树;
  • 返回 nums 构建的 最大二叉树 。

显而易见,我可以把构建最大二叉树这个大问题,划分为一个个小问题,当每一棵子树都按照上述思路来构建,那么整体就构成了一棵最大二叉树,小问题思路如下:

  • 数组 nums 有三种情况:
    • 数组长度为0:说明节点序列为空,返回 null
    • 数组长度为1:说明只有一个节点,构建并返回节点
    • 数组长度 > 1:找出最大值来构建当前节点
  • 找出nums中的最大值,以及最大值的下标
  • 按照要求,通过最大值的下标来划分构建左右子树的子数组
  • 左子树通过最大值左边的子数组来构建
  • 右子树通过最大值右边的子数组来构建

递归从根节点开始到左右子树构建子树,最后返回构建完成的根节点。

Java解法(递归,深度优先遍历):

class Solution {
    public int findMaxIndex(int[] nums){
        int res = -1, max = -1;
        for(int i = 0; i < nums.length; i++){
            if(max < nums[i]){
                max = nums[i];
                res = i;
            }
        }
        return res;
    }

    public TreeNode constructMaximumBinaryTree(int[] nums) {
        if(nums.length == 0)
            return null;
        if(nums.length == 1)
            return new TreeNode(nums[0]);
        int max_i = this.findMaxIndex(nums);
        TreeNode root = new TreeNode(nums[max_i]);
        root.left = this.constructMaximumBinaryTree(Arrays.copyOfRange(nums, 0, max_i));
        root.right = this.constructMaximumBinaryTree(Arrays.copyOfRange(nums, max_i+1, nums.length));
        return root;
    }
}

合并二叉树

题目详细:LeetCode.617

通过合并二叉树的过程,不难发现,在合并过程中会出现这4种情况(这里将要合并的树记作 root1 和 root2,让root2合并到root1):

  • root1 != null && root2 != null,那么两个节点的值相加,root1.val += root2.val;
  • root1 == null && root2 != null,左节点为空,那么直接将 root2 合并到 root1 中;
  • root1 != null && root2 == null,右节点为空,那么无需合并,直接返回左节点
  • root1 == null && root2 == null,左右节点都为空,无需合并或二叉树已完成合并

递归从根节点到左右子树开始合并子树,最后返回合并完成的根节点(root1)。

Java解法(递归,深度优先遍历):

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

二叉搜索树中的搜索

题目详细:LeetCode.700

二叉搜索树的特点可以简要概括为:

  • 从根节点开始构建一棵二叉树,使其每一棵子树都满足
    • 左节点的值 < 父节点的值
    • 右节点的值 > 父节点的值
  • 【注意】所以一棵真正的二叉搜索树应满足:
    • 左子树上所有的节点值都 < 父节点的值
    • 右子树上所有的节点值都 > 父节点的值
  • 那么我们则称这棵树为二叉搜索树

那么这道题要求在二叉搜索树中找到目标值对应的子树,利用二叉搜索树的特点和递归来解题就非常清晰易懂了,在搜索过程中会遇到这3种情况:

  • 节点为空,则二叉树中不到目标值节点,返回null
  • 节点的值 > 目标值,说明目标值只可能在二叉搜索树的右子树出现,递归搜索右子树
  • 节点的值 < 目标值,说明目标值只可能在二叉搜索树的左子树出现,递归搜索左子树
  • 节点的值 = 目标值,说明当前节点就是我们要找的子树的根节点,返回该节点

Java解法(递归,深度优先遍历):

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

验证二叉搜索树

题目详细:LeetCode.98

Java(错误的解法,陷进了一个大坑):

class Solution {
    public boolean isValidBST(TreeNode root) {
        return this.dfs(root.left, root.val, true) && this.dfs(root.right, root.val, false);
    }

    public boolean dfs(TreeNode root, int father_val, boolean is_left){
        if(root == null) return true;
        boolean flag = false;
        if(is_left){
            flag = root.val < father_val
        }else{
            flag = root.val > father_val
        }
        return flag && this.dfs(root.left, root.val, true) && this.dfs(root.right, root.val, false);
    }
}

一开始我发现这道题很简单,不就是根据二叉搜索树的特点来验证一棵二叉树是不是搜索树么,然后就得出结论:

  • 递归验证每一个树的节点是否满足二叉搜索树的特点
  • 二叉搜索树的特点在上一题有讲述,这里就不做赘述了
  • 如果每一个节点都满足,那么则视作整体满足,返回 true

接着写完我就非常自信地提交了答案!很好,错误的测试例子出现了:

在这里插入图片描述
在这里插入图片描述
验证了一下算法,发现到达节点值为3的节点时,应该返回 false,因为假如构建一棵二叉搜索树的话,节点3应该是节点4的左节点才对。

所以:

  • 不能单纯地对每一个节点判断其是否满足左节点 < 父节点,右节点 > 父节点就完事了。
  • 一棵真正的二叉搜索树还需要满足的条件是左子树所有节点 < 父节点,右子树所有节点 > 父节点

从二叉搜索树的特点,还可以发现其中序遍历序列,是一个递增序列,那么解题方法忽如一夜春风来,千树万树梨花开呀:

  • 解法一:
    • 我们可以直接对该二叉树进行中序遍历,获取该树的中序遍历序列;
    • 判断该遍历序列是否是递增的,如果是递增序列,则说明该树是二叉搜索树,否则不是。

Java解法(中序遍历,验证二叉搜索树的中序遍历序列是否是递增序列):

class Solution {
    List<Integer> inorder_list = new ArrayList<>();

    public void dfs(TreeNode root){
        if(root == null) return;
        dfs(root.left);
        inorder_list.add(root.val);
        dfs(root.right);
    }

    public boolean isValidBST(TreeNode root) {
        this.dfs(root);
        int pre = Integer.MIN_VALUE;
        for(int i: this.inorder_list){
            if(pre > i) return false;
            pre = i;
        }
        return true;
    }
}
  • 解法二:
    • 从解法一可知,我们可以通过验证中序遍历序列是否递增,进而判断该树是否为二叉搜索树
    • 那么我们也可以模拟这一思想,改用迭代的算法能够一边遍历节点,一边处理节点
    • 一边迭代遍历二叉树,一边比较与前一个遍历的值,来判断遍历序列是否有序,进而判断该树是否为二叉树

Java解法(中序遍历,模拟,迭代,一边遍历一边比较):

class Solution {
    public boolean inorder(TreeNode root){
        Stack<TreeNode> stack = new Stack<>();
        if(null != root) stack.push(root);
        TreeNode pre = null;
        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            if(null != node){
            	// 以右根左的顺序进栈,出栈顺序为左根右
                if(null != node.right) stack.push(node.right);
                stack.push(node);
                stack.push(null);
                if(null != node.left) stack.push(node.left);
            }else{
                node = stack.pop();
                if(null != pre && node.val <= pre.val) return false;
                pre = node;
            }
        }
        return true;
    }

    public boolean isValidBST(TreeNode root) {
        return this.inorder(root);
    }
}

天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣。人之为学有难易乎?学之,则难者亦易矣;不学,则易者亦难矣。

天下的事情有困难和容易的区别吗?只要肯做,那么困难的事情也变得容易了;如果不做,那么容易的事情也变得困难了。人们做学问有困难和容易的区别吗?只要肯学,那么困难的学问也变得容易了;如果不学,那么容易的学问也变得困难了。

这首诗出自清代彭端淑的《为学一首示子侄》,最近我一直在努力学习,但又一直感觉自己为面试的准备工作很不充分,不敢大胆投简历,也不敢去面试,看完这首诗之后,恍然大悟:

“世上无难事,只怕有心人。”,亦是同样的道理,困难是可以克服的,慢慢来吧。

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

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

相关文章

计算机系统基础知识

计算机的基本组成 计算机组成逻辑图 计算机部件作用 一级部件作用 运算器&#xff1a;计算机的执行部件&#xff0c;受控制器控制&#xff0c;执行算术运算或逻辑运算控制器&#xff1a;决定计算机运行过程的自动化。不仅能保证程序指令的正确执行&#xff0c;还能处理异常事…

12款适合小团队协作、任务管理和进度跟踪的在线任务管理的工具推荐?

国内外12款主流任务管理软件测评: 1.开发任务管理PingCode; 2.多合一项目任务管理Worktile;3.个人和小团队项目任务管理Notion; 4.企业任务管理平台SmartTask; 5.小团队任务管理Teambition;6.IT任务追踪管理Jira等。无论是做好工作任务管理还是个人任务管理&#xff0c;从来都不…

web网页如何实现响应式导航栏--移动端导航栏

背景&#xff1a; 一提到响应式导航栏&#xff0c;大家第一反应可能就是bootstrap响应式导航栏&#xff0c;这个响应式的一般是针对屏幕变小时&#xff0c;视口出现导航栏&#xff0c;可是&#xff0c;展示到移动端的时候&#xff0c;并没有变化&#xff1f;&#xff1f;&#…

LabVIEW利用矢量量化直方图开发人脸识别

LabVIEW利用矢量量化直方图开发人脸识别通常&#xff0c;人脸识别系统会检查场景的静止图像或视频图像&#xff0c;然后使用存储的人脸数据库识别或验证场景中的一个或多个人。我程序专注于静止图像人脸识别&#xff0c;使用来自众所周知的人脸数据库的人脸图像&#xff0c;用于…

Prometheus之pushgateway

Pushgateway简介 Pushgateway是Prometheus监控系统中的一个重要组件&#xff0c;它采用被动push的方式获取数据&#xff0c;由应用主动将数据推送到pushgateway&#xff0c;然后Prometheus再从Pushgateway抓取数据。使用Pushgateway的主要原因是&#xff1a; Prometheus和targ…

Teradata 离场,企业数据分析平台如何应对变革?

近日大数据分析和数仓软件巨头 Teradata&#xff08;TD&#xff09;宣布基于中国商业环境的评估&#xff0c;退出在中国的直接运营。TD 是全球最大的专注于大数据分析、数仓和整合营销管理解决方案的供应商之一&#xff0c;其早在 1997 年就进入中国&#xff0c;巅峰期占据半数…

基于卷积神经网络图像风格迁移系统的设计与实现(flask系统)

1.摘要 Leon Gatys 等人研发的深度神经网络使用神经的表达来分离任意图片的内容和风格&#xff0c;为生成艺术图片提供一个神经算法。本文基于Style Transfer算法&#xff0c;使用风格成本函数训练CNN&#xff0c;用卷积神经网络提取图像特征&#xff0c;依次提取内容图像的内…

数据库面试——锁的12连问,赶紧收藏!

目录 1. 为什么需要加锁 2. InnoDB有哪些锁&#xff1f; 2.1 共享/排他锁 2.2 意向锁 2.3 记录锁&#xff08;Record Lock&#xff09; 2.4 间隙锁&#xff08;Gap Lock&#xff09; 2.5 临键锁(Next-Key Lock) 2.6 插入意向锁 2.7 自增锁 3. 什么是死锁&#xff1f;如…

从红队视角看AWD攻击

AWD的权限维持 攻防兼备AWD模式是一种综合考核参赛团队攻击、防御技术能力、即时策略的比赛模式。在攻防模式中&#xff0c;参赛队伍分别防守同样配置的虚拟靶机&#xff0c;并在有限的博弈时间内&#xff0c;找到其他战队的薄弱环节进行攻击&#xff0c;同时要对自己的靶机环…

20230220华南金牌主板u盘启动

20230220华南金牌主板u盘启动 2023/2/20 10:29 百度搜索&#xff1a;华南金牌主板u盘启动 https://www.zhihu.com/question/498121895?utm_id0 华南金牌主板b85u盘启动怎么设置? 华南金牌主板b85u盘启动怎么设置 海的那边 上小学后才发现还是幼儿园好混…… 华南一般是F7和F1…

ADRC自抗扰控制总结

目录 前言 1.ADRC形式 1.1形一 1.2形二 2.被控对象 3.仿真分析 3.1仿真模型 3.2仿真结果 4.学习问题 前言 前面的3篇文章依次介绍了微分跟踪器TD、状态观测器ESO和非线性状态误差反馈NLSEF三部分内容&#xff0c;至此ADRC的结构已经介绍完毕&#xff0c;现在对分块学习…

【数据结构与算法】2.八大经典排序

文章目录简介1.分析排序算法2.插入排序2.1.直接插入排序2.2.希尔排序3.选择排序3.1.直接选择排序3.2.堆排序3.2.1.堆的数据结构3.2.2.算法实现4.交换排序4.1.冒泡排序4.2.快速排序5.归并排序6.基数排序7.八大排序算法总结简介 排序对于任何一个程序员来说&#xff0c;可能都不会…

从技术上来看,互联网技术开始衍生和蜕变出更多的新技术

很多人在看待产业互联网的问题上&#xff0c;一味地割裂它与互联网之间的联系&#xff0c;甚至还有人将产业互联网看成是对于传统互联网的颠覆。如果仅仅只是以这样的眼光来看待产业互联网&#xff0c;那么&#xff0c;他们势必是无法完整把握产业互联网的本质内涵和原始奥义的…

2023什么是分销商城?怎么搭建分销商城

当实体机构都接连探索线上营销模式的时候&#xff0c;分销也随着社交电商的兴起应运而生。 大家好&#xff0c;我是你们熟悉而又陌生的好朋友梦龙&#xff0c;一个创业期的年轻人 它借助裂变效率高的属性&#xff0c;建立更多用户触点&#xff0c;更好的提升企业运营的势能&am…

ros-sensor_msgs/PointCloud2消息内容解释

1.字段解释 header-----头文件&#xff0c;包含消息的序列号&#xff0c;时间戳(系统时间)和坐标系id&#xff0c;其中secs为秒&#xff0c;nsecs为去除秒数后剩余的纳秒数 height-----点云的高度&#xff0c;如果是无序点云&#xff0c;则为1&#xff0c;例子中的点云为有序点…

8 狗监控的封装

概述 为了保证嵌入式程序能够长时间稳定地运行,需要加入狗监控机制。狗监控的原理为:应用程序需要每隔一段时间来喂狗或保活,如果应用程序崩溃或者内核崩溃,导致长时间无法喂狗,则狗将超时,会自动重启系统。部分IPC芯片提供了硬件狗,对于没有硬件狗的,需要自行实现软件…

在代码质量和工作效率的矛盾间如何取舍?

这个问题的答案是&#xff0c;在很短的一段时期&#xff0c;编写高质量代码似乎会拖慢我们的进度。与按照头脑中首先闪现的念头编写代码相比&#xff0c;高质量的代码需要更多的思考和努力。但如果我们编写的不仅仅是运行一次就抛之脑后的小程序&#xff0c;而是更有实质性的软…

移动web(二)

her~~llo&#xff0c;我是你们的好朋友Lyle&#xff0c;是名梦想成为计算机大佬的男人&#xff01; 博客是为了记录自我的学习历程&#xff0c;加强记忆方便复习&#xff0c;如有不足之处还望多多包涵&#xff01;非常欢迎大家的批评指正。 目录 一、移动端特点 1.1 移动端和…

mysql调优参数

my.conf [client] port 端口 socket sokcet位置 [mysqld] basedir mysql位置 port 3306 socket sokcet位置 datadir data目录 pid_file mysqld.pid位置 bind_address 0.0.0.0 lower_case…

【Spark分布式内存计算框架——Spark SQL】14. 分布式SQL引擎

第八章 分布式SQL引擎 回顾一下&#xff0c;如何使用Hive进行数据分析的&#xff0c;提供哪些方式交互分析&#xff1f;&#xff1f;&#xff1f; 方式一&#xff1a;交互式命令行&#xff08;CLI&#xff09; bin/hive&#xff0c;编写SQL语句及DDL语句 方式二&#xff1a…