代码随想录算法训练营day17||二叉树part04、110.平衡二叉树 、257. 二叉树的所有路径 、404.左叶子之和

news2025/1/19 8:13:16
注意:迭代法,可以先过,二刷有精力的时候 再去掌握迭代法。

110.平衡二叉树 (优先掌握递归)

再一次涉及到,什么是高度,什么是深度,可以巩固一下。

题目:给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

示例 1:给定二叉树 [3,9,20,null,null,15,7]    返回 true 。  示例2:返回false

题外话

咋眼一看这道题目和104.二叉树的最大深度 (opens new window)很像,其实有很大区别。

这里强调一波概念:

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。

但leetcode中强调的深度和高度很明显是按照节点来计算的,如图:

注意:关于根节点的深度究竟是1 还是 0,不同的地方有不一样的标准,leetcode的题目中都是以节点为一度,即根节点深度是1。但维基百科上定义用边为一度,即根节点的深度是0,我们暂时以leetcode为准(毕竟要在这上面刷题)。

因为求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)

有的同学一定疑惑,为什么104.二叉树的最大深度 (opens new window)中求的是二叉树的最大深度,也用的是后序遍历。

那是因为代码的逻辑其实是求的根节点的高度,而根节点的高度就是这棵树的最大深度,所以才可以使用后序遍历。

本题思路:(此时大家应该明白了既然要求比较高度,必然是要后序遍历。)采用递归法

递归三步曲分析:

1.明确递归函数的参数和返回值

参数:当前传入节点。 返回值:以当前传入节点为根节点的树的高度。

那么如何标记左右子树是否差值大于1呢?

如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。

所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。

2.明确终止条件

递归的过程中依然是遇到空节点了为终止,返回0,表示当前节点为根节点的树高度为0

3.明确单层递归的逻辑

如何判断以当前传入节点为根节点的二叉树是否是平衡二叉树呢?当然是其左子树高度和其右子树高度的差值。

分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则返回-1,表示已经不是二叉平衡树了。

class Solution {
    public boolean isBalanced(TreeNode root) {
        //递归法
        return getHeight(root) != -1;
    }

    private int getHeight(TreeNode root){
        if(root == null ) return 0;

        int leftHeight = getHeight(root.left);
        if(leftHeight == -1) return -1;

        int rightHeight = getHeight(root.right);
        if(rightHeight == -1) return -1;

        //左右子树高度差大于1,return -1表示已经不是平衡二叉树了
        if(Math.abs(leftHeight - rightHeight) > 1)  {
            return -1;
        }
        return Math.max(leftHeight,rightHeight) + 1;
    }
}

257. 二叉树的所有路径 (优先掌握递归)  

这是大家第一次接触到回溯的过程, 我在视频里重点讲解了 本题为什么要有回溯,已经回溯的过程。 如果对回溯 似懂非懂,没关系, 可以先有个印象。 

题目:给定一个二叉树,返回所有从根节点到叶子节点的路径。

说明: 叶子节点是指没有子节点的节点。

思路

这道题目要求从根节点到叶子的路径,所以需要前序遍历这样才方便让父节点指向孩子节点,找到对应的路径。

在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一个路径再进入另一个路径。

前序遍历以及回溯的过程如图:

我们先使用递归的方式,来做前序遍历。 要知道递归和回溯就是一家的,本题也需要回溯。
class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        //递归法
        List<String> res = new ArrayList<>(); //用于存储最终结果集的List,其中每个元素都是一个表示路径的字符串。
        if(root == null)  return res;
        List<Integer> paths = new ArrayList<>();//用于在递归过程中构建路径的List,其中每个元素都是二叉树节点的值。
        traversal(root,paths,res);
        return res;
    }
    //traversal 方法是递归的核心部分,它接受当前节点、路径列表和结果列表作为参数。
    private void traversal(TreeNode root,List<Integer> paths,List<String> res){
        paths.add(root.val); //前序遍历,中!
        //遇到叶子节点时(左右子树都为空的节点)
        if(root.left == null && root.right == null){
            //输出
            StringBuilder sb = new StringBuilder();//创建一个StringBuilder对象sb用来拼接字符串,速度更快
            //遍历路径列表,将paths中的元素按顺序连接成一个路径,并将该路径添加到res集合中。
            for(int i = 0;i < paths.size()-1; i++){ 
                sb.append(paths.get(i)).append("->");
            }
            sb.append(paths.get(paths.size() - 1));//记录最后一个节点
            res.add(sb.toString());//收集一个路径
            return;
        }
        //递归和回溯是同时进行的,所以要放在同一个花括号里
        if(root.left != null){ 
            traversal(root.left,paths,res); //左
            paths.remove(paths.size() - 1); //回溯
        }
        if(root.right != null){
            traversal(root.right,paths,res); //右
            paths.remove(paths.size() - 1); //回溯
        }
    }
}

注意:

  • traversal 方法:

    • traversal 方法是递归的核心部分。它接受当前节点、路径列表和结果列表作为参数。
    • 将当前节点的值添加到路径列表中。
    • 如果当前节点是叶子节点(左右子节点都为空),则执行以下操作:
      • 创建一个 StringBuilder 对象 sb
      • 通过遍历路径列表,将节点值连接成路径字符串,并使用 -> 分隔。
      • 将路径字符串添加到结果列表中。
    • 无论当前节点是否为叶子节点,都会递归地对左子节点和右子节点执行相同的操作(如果存在的话)。在递归之后,会通过 paths.remove(paths.size() - 1); 进行回溯,移除刚刚添加的节点值,以恢复到上一层的状态。

这行代码 paths.remove(paths.size() - 1);

是在回溯过程中使用的。在递归遍历左子树或右子树之后,程序需要回到当前节点的父节点,继续遍历其他子节点或者回溯到更上层的节点。

因为 paths 列表记录了从根节点到当前节点的路径,当递归返回到父节点时,需要将当前节点从路径中移除,以便继续探索其他分支或者回溯到更上层的节点,保证 paths 列表中记录的是当前路径上的节点序列。这就是为什么在回溯时,需要移除 paths 列表中的最后一个元素的原因。

class Solution {
    //方式二:递归法,精简版,并隐藏了回溯过程
    List<String> result = new ArrayList<>();//用于存储最终结果集的List,其中每个元素都是一个表示路径的字符串。

    public List<String> binaryTreePaths(TreeNode root) {
        traversal(root, "");
        return result;
    }
    public void traversal(TreeNode node, String sb) {
        //当前节点为空时
        if (node == null)
            return;
        //当前节点为 叶子节点时,将当前节点的值添加到字符串s后面,并将整个字符串添加到结果列表中。
        if (node.left == null && node.right == null) {
            result.add(new StringBuilder(sb).append(node.val).toString()); //中
            return;
        }
        //创建一个临时字符串 tmp,它是在字符串s后面添加了当前节点的值和箭头 ->。
        String tmp = new StringBuilder(sb).append(node.val).append("->").toString();  
        traversal(node.left, tmp);  //左
        traversal(node.right, tmp); //右
    }
}
  1. traversal 方法:

    • 如果当前节点为空,直接返回。
    • 如果当前节点是叶子节点(左右子节点都为空),则将当前节点的值添加到字符串 s 后面,并将整个字符串添加到结果列表中。
    • 创建一个临时字符串 tmp,它是在字符串 s 后面添加了当前节点的值和 ->
    • 然后分别对左子节点和右子节点递归调用 traversal 方法,并将临时字符串 tmp 作为参数传递下去。
  2. 总体逻辑:

    • 通过递归调用 traversal 方法,在每个叶子节点处将路径字符串添加到结果列表中。
    • 递归过程中使用临时字符串来构建路径,简化了代码的实现。

这种方法的核心思想与之前的代码类似,都是通过递归遍历二叉树,并在叶子节点处构建路径字符串。但是,这个精简版的代码隐藏了回溯过程,通过临时字符串tmp和直接在叶子节点处添加路径字符串来简化了代码的结构。

404.左叶子之和 (优先掌握递归)

其实本题有点文字游戏,搞清楚什么是左叶子,剩下的就是二叉树的基本操作。 

计算给定二叉树的所有左叶子之和。

思路

首先要注意是判断左叶子,不是二叉树左侧节点,所以不要上来想着层序遍历。

因为题目中其实没有说清楚左叶子究竟是什么节点,那么我来给出左叶子的明确定义:节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点

大家思考一下如下图中二叉树,左叶子之和究竟是多少? 没有左叶子!

 再看这个图的左叶子之和是多少?

相信通过这两个图,大家对最左叶子的定义有明确理解了。

那么判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子。

如果该节点的左节点不为空,该节点的左节点的左节点为空,该节点的左节点的右节点为空,则找到了一个左叶子,判断代码如下:

if (node->left != NULL && node->left->left == NULL && node->left->right == NULL) {
    左叶子节点处理逻辑
}

递归法

递归的遍历顺序为后序遍历(左右中),是因为要通过递归函数的返回值来累加求取左叶子数值之和。

递归三部曲:

1.确定递归函数的参数和返回值

判断一个树的左叶子节点之和,那么一定要传入树的根节点,递归函数的返回值为数值之和,所以为int

使用题目中给出的函数就可以了。

2.确定终止条件

如果遍历到空节点,那么左叶子值一定是0

注意,只有当前遍历的节点是父节点,才能判断其子节点是不是左叶子。 所以如果当前遍历的节点是叶子节点,那其左叶子也必定是0,那么终止条件为:

if (root == NULL) return 0;
if (root->left == NULL && root->right== NULL) return 0; //其实这个也可以不写,如果不写不影响结果,但就会让递归多进行了一层。

3.确定单层递归的逻辑

当遇到左叶子节点的时候,记录数值,然后通过递归求取左子树左叶子之和,和 右子树左叶子之和,相加便是整个树的左叶子之和。

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        //递归法,后序遍历
        if(root == null) return 0;
        int leftValue = sumOfLeftLeaves(root.left); //左
        int rightValue = sumOfLeftLeaves(root.right); //右

        int midValue = 0;
        //判断左叶子的关键语句,该节点的左节点不为空,但左节点的左节点和左节点的右节点为空! 
        //则把该节点的左节点赋值给midValue
        if(root.left != null && root.left.left == null && root.left.right == null){
            midValue = root.left.val;
        }
        int sum = midValue + leftValue + rightValue; //中
        return sum;
    }
}

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

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

相关文章

Error creating bean with name ‘formContentFilter‘ defined in class path

问题描述 运行之前能正常的项目出现以上报错&#xff0c;提示创建“formContentFilter”时错误&#xff1b;org.springframework.boot版本2.4.8 org.springframework.beans.factory.BeanCreationException. Message: Error creating bean with name formContentFilter define…

Hadoop-Yarn-调度器总结

一、Yarn有哪些调度器 在cdh中Yarn组件中查看配置如下&#xff1a; 可以看到Yarn有三种调度器&#xff0c;分别是FairScheduler、FifoScheduler、CapacityScheduler&#xff0c;它们都是Hadoop的一个可插入调度器。 cdh默认的调度器是FairScheduler&#xff0c;hadoop默认的调…

Process Explorer下载安装使用教程(图文教程)超详细

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 Process Explore 是微软的一款「进程资源管理器」&#xff0c;比Windows系统自带的任务管…

2024年最适合计算机专业看的三部电影

2024年最适合计算机专业看的三部电影 计算机专业必看的几部电影&#xff0c;我推荐的三部电影&#xff0c;分别是&#xff1a;黑客帝国4&#xff1a;矩阵重启、沙丘2和头号玩家。 其中《沙丘2》2024-03-08上映。 1. 黑客帝国4&#xff1a;矩阵重启 (2021年上映) 为什么推荐…

高效Excel操作:Python开发者的指南

高效Excel操作&#xff1a;Python开发者的指南 引言Python与Excel交互的基础知识选择合适的库安装Python库 安装与设置1. 安装openpyxl2. 安装xlrd和xlwt3. 安装pandas4. 安装xlsxwriter环境验证 读取Excel文件使用openpyxl读取xlsx文件使用xlrd读取xls文件使用pandas读取Excel…

安全名词解析-攻防演练

为方便您的阅读&#xff0c;可点击下方蓝色字体&#xff0c;进行跳转↓↓↓ 01 攻防演练 01 攻防演练 《网络安全法》中明确提出&#xff0c;“定期组织关键信息基础设施的运营者进行网络安全应急演练&#xff0c;提高应对网络安全事件的水平和协同配合能力。”攻防演练目前已经…

maven异常记录-must be unique

maven 打包异常记录 我们可以看看一个重要的异常&#xff1a; dependencies.dependency.(groupId:artifactId:type:classifier) must be unique: org.springframework.boot:spring-boot-starter-test 经过检查pom文件 果然是spring-boot-starter-test引用重复&#xff0c;平…

使用阿里云发送短信

使用阿里云短信服务有两种方式 API 发送和 控制台发送&#xff0c;控制台发送到话有太多限制&#xff0c;这里我们使用API 通过 调用服务端代码进行发送。 整体结构如下&#xff1a; 导入依赖 <!--阿里云短信发送--><dependency><groupId>com.aliyun<…

TrueNAS磁盘扩容(VDEV 和 RAID 技术)

目录 背景扩容前准备扩容有风险安装新的硬盘到卡槽扩容测试一个VDEV两个VDEV 正式扩容关于Raid总结 背景 这几天将原来windows服务器上的文件拷贝到新做好的TrueNAS上&#xff0c;发现磁盘满了&#xff0c;服务器上还有硬盘卡槽&#xff0c;就新买了12块盘&#xff0c;准备扩容…

人工智能|深度学习——基于对抗网络的室内定位系统

代码下载&#xff1a; 基于CSI的工业互联网深度学习定位.zip资源-CSDN文库 摘要 室内定位技术是工业互联网相关技术的关键一环。该技术旨在解决于室外定位且取得良好效果的GPS由于建筑物阻挡无法应用于室内的问题。实现室内定位技术&#xff0c;能够在真实工业场景下实时追踪和…

小程序--本地存储API

1、存储数据 wx.setStorageSync()&#xff1a;无需转换数据类型&#xff0c;存什么类型的就是什么类型的&#xff0c;data中的数据&#xff0c;使用时是this.data.名称。 saveData() {wx.setStorageSync(list, this.data.list)wx.showToast({title: 存储成功,})}, 2、读取数据…

163邮箱发邮件

1、Jenkins安装Email Extension Plugin 2、网易邮箱里获取授权码:qa_jenkins_robot@163.com 开启POP3/SMTP 我已经配置过了,所以这里会有一个使用设备 3、配置Jenkins邮箱通知 Manage Jnekins-Configuration System Jenkins Location: Extended E-mail Notification: …

关于三维GIS开发成长路线的一些思考

三维GIS是将GIS三维化表达&#xff0c;从一个三维GIS开发门外汉的角度来看&#xff0c;三维GIS开发成长路线分几个层面&#xff1a; 第一层面 做三维开发&#xff0c;最基本的Cesium、ThreeJS、MapBox这些要能做到接口级熟悉&#xff0c;熟悉接口是用来干嘛的&#xff0c;接口…

HarmonyOS4.0系列——08、整合UI常用组件

HarmonyOS4.0 系列——08、UI 组件 Blank Blank 组件在横竖屏占满空余空间效果 // xxx.ets Entry Component struct BlankExample {build() {Column() {Row() {Text(Button).fontSize(18)Blank()Toggle({type: ToggleType.Switch}).margin({top: 14,bottom: 14,left: 6,righ…

Camtasia 2023 v23.4.2.51146 Win功能强大的屏幕录制和视频编辑软件

Camtasia 2023.3.4.2是一款适用于各类用户的屏幕录制和视频编辑软件&#xff0c;特别适合需要制作教育、培训和营销视频的专业人士。它结合了易用性和多样的功能&#xff0c;使视频制作变得更加高效和专业。 软件安装 适用于 Win10.win11系统 1.直接安装软件&#xff0c;安装…

自定义异常处理演示

​ 为了防止黑客从前台异常信息&#xff0c;对系统进行攻击。同时&#xff0c;为了提高用户体验&#xff0c;我们都会都抛出的异常进行拦截处理。 一、全局异常处理 编写一个异常拦截类&#xff0c;如下&#xff1a;ControllerAdvice&#xff0c;很多初学者可能都没有听说过…

【Go语言】Go语言中的变量和常量

Go语言中的变量和常量 1 变量 变量相当于是对一块数据存储空间的命名&#xff0c;程序可以通过定义一个变量来申请一块数据存储空间&#xff0c;之后可以通过引用变量名来使用这块存储空间。 Go 语言是强类型静态语言&#xff0c;所以变量的声明与赋值方式与 PHP/Python 等动…

极狐GitLab 如何配置多个 LDAP?

本文仅适用于极狐GitLab私有化部署场景。 场景化痛点 极狐GitLab 的多 LDAP 接入功能解决了企业在以下场景中可能遇到的痛点&#xff1a; 多个组织/部门的整合&#xff1a;在大型企业或跨国公司中&#xff0c;往往存在多个组织或部门&#xff0c;它们可能拥有独立的 LDAP 服务…

kuka示教器嵌套UR界面操作ros中rviz的UR机器人

摘要 本例展示了用QT增加一个网页视图&#xff0c;背景是kuka示教器界面&#xff0c;中间增加UR的VNC网页界面显示。本人博客中一起有写过ros2运行UR的操作。 ros2 UR10仿真包运行_基于ros的ur仿真-CSDN博客 效果如下&#xff1a; 1.打开UR机器人的ros2仿真文件 sudo su ros2…

【Python数据分析系列】实现dataframe逐行遍历和逐列遍历(案例+源码)

一、引言 在处理和分析表格数据时&#xff0c;我们经常需要对数据进行遍历&#xff0c;然后做进一步后续处理&#xff0c;这里涉及到表格数据的逐行遍历或者逐列遍历的操作&#xff0c;本文将通过一个案例教你实现这个操作。 二、实现过程 准备数据 代码&#xff1a; # 准备…