代码随想录-Day16
104. 二叉树的最大深度
方法一:深度优先搜索
class Solution {public int maxDepth(TreeNode root) {if (root == null) {return 0;} else {int leftHeight = maxDepth(root.left);int rightHeight = maxDepth(root.right);return Math.max(leftHeight, rightHeight) + 1;}}
}
这段代码定义了一个名为 Solution
的类,其中包含一个方法 maxDepth
用于计算二叉树的最大深度。最大深度是从根节点到最远叶子节点的最长路径上的边数。方法使用了递归的策略来实现这一计算。以下是代码的详细解析:
- 方法签名:
public int maxDepth(TreeNode root)
- 输入参数:
TreeNode root
- 二叉树的根节点。 - 输出:
返回类型为int
的最大深度值。
代码逻辑:
-
基本情况处理:
- 首先检查根节点
root
是否为空 (if (root == null)
)。如果树为空,则没有节点,深度为0,所以直接返回0。
- 首先检查根节点
-
递归计算左右子树深度:
- 如果根节点不为空,递归地计算左子树的最大深度 (
int leftHeight = maxDepth(root.left);
) 和右子树的最大深度 (int rightHeight = maxDepth(root.right);
)。
- 如果根节点不为空,递归地计算左子树的最大深度 (
-
确定整棵树的最大深度:
- 使用
Math.max(leftHeight, rightHeight)
来获取左、右子树中较大的深度值,然后加1(因为要包括根节点的高度),得到整棵树的最大深度。 - 最后,返回这个计算出的最大深度值。
- 使用
通过递归调用,该方法能够遍历到二叉树的每一个节点,并通过比较左右子树的深度来确定整棵树的最大深度,是一种分治策略的典型应用。
方法二:广度优先搜索
class Solution {public int maxDepth(TreeNode root) {if (root == null) {return 0;}Queue<TreeNode> queue = new LinkedList<TreeNode>();queue.offer(root);int ans = 0;while (!queue.isEmpty()) {int size = queue.size();while (size > 0) {TreeNode node = queue.poll();if (node.left != null) {queue.offer(node.left);}if (node.right != null) {queue.offer(node.right);}size--;}ans++;}return ans;}
}
这段代码定义了一个名为 Solution
的类,其中包含一个方法 maxDepth
用于计算二叉树的最大深度,即树中最长路径的边数。这里使用了广度优先搜索(BFS)策略来实现,具体解析如下:
- 方法签名:
public int maxDepth(TreeNode root)
- 输入参数:
TreeNode root
- 二叉树的根节点。 - 输出:
返回类型为int
的最大深度值。
代码逻辑:
-
基本情况处理:
- 首先检查根节点
root
是否为空 (if (root == null)
)。如果是,表明树为空,深度为0,直接返回0。
- 首先检查根节点
-
初始化队列与广度优先遍历:
- 创建一个队列
queue
,并将根节点root
入队列 (queue.offer(root);
),用于开始广度优先遍历。 - 定义一个变量
ans
用于存储当前已遍历的层数,初始化为0。 - 当队列非空时,进行循环,意味着还有节点未遍历:
- 获取当前层的节点数
size
,即队列的大小。 - 通过内层循环遍历当前层的所有节点:
- 弹出队首节点
node
,处理该节点(实际上这里直接弹出,没有具体操作,重点在于处理其子节点)。 - 若该节点的左子节点不为空,则左子节点入队列。
- 若该节点的右子节点不为空,则右子节点入队列。
size
减1,表示当前层的一个节点已被处理完毕。
- 弹出队首节点
- 当一层处理完后,
ans
增加1,表示已遍历完一层。
- 获取当前层的节点数
- 创建一个队列
-
返回结果:
- 当所有节点遍历完毕,队列为空时,返回
ans
作为最大深度。
- 当所有节点遍历完毕,队列为空时,返回
此算法通过BFS遍历二叉树,每遍历完一层深度加1,最终得到的 ans
即为树的最大深度,这种方式适用于任何形态的二叉树结构,包括不平衡树。
111. 二叉树的最小深度
方法一:深度优先搜索
class Solution {public int minDepth(TreeNode root) {if (root == null) {return 0;}if (root.left == null && root.right == null) {return 1;}int min_depth = Integer.MAX_VALUE;if (root.left != null) {min_depth = Math.min(minDepth(root.left), min_depth);}if (root.right != null) {min_depth = Math.min(minDepth(root.right), min_depth);}return min_depth + 1;}
}
这段代码是 Java 语言实现的一个解决方案,用于计算二叉树的最小深度。给定一个二叉树,最小深度是从根节点到最近叶子节点的最短路径上的边数。这里使用了递归的方法来解决这个问题。下面是对代码的详细解释:
-
public int minDepth(TreeNode root)
定义了一个公开方法,接受一个 TreeNode 类型的参数root
,表示二叉树的根节点,返回值为最小深度。 -
首先检查
root == null
,如果根节点为空,则说明这是一个空树,其深度为 0,所以返回 0。 -
然后检查
root.left == null && root.right == null
,如果当前节点既没有左子节点也没有右子节点,说明当前节点就是叶子节点,此时深度为 1,所以返回 1。 -
接下来定义一个变量
min_depth
初始化为Integer.MAX_VALUE
,用于保存左右子树中的最小深度。 -
通过条件语句
if (root.left != null)
检查左子节点是否存在,如果存在,则递归调用minDepth(root.left)
获取左子树的最小深度,并更新min_depth
的值。 -
同样,通过条件语句
if (root.right != null)
检查右子节点是否存在,如果存在,则递归调用minDepth(root.right)
获取右子树的最小深度,并更新min_depth
的值。 -
最后,返回
min_depth + 1
作为整棵树的最小深度。这里的 “+1” 是因为需要加上从父节点到当前节点的这一边。
整个函数通过递归遍历整棵二叉树,逐步计算并比较左右子树的最小深度,从而找到从根节点到最近叶子节点的最短路径长度。
方法二:广度优先搜索
class Solution {class QueueNode {TreeNode node;int depth;public QueueNode(TreeNode node, int depth) {this.node = node;this.depth = depth;}}public int minDepth(TreeNode root) {if (root == null) {return 0;}Queue<QueueNode> queue = new LinkedList<QueueNode>();queue.offer(new QueueNode(root, 1));while (!queue.isEmpty()) {QueueNode nodeDepth = queue.poll();TreeNode node = nodeDepth.node;int depth = nodeDepth.depth;if (node.left == null && node.right == null) {return depth;}if (node.left != null) {queue.offer(new QueueNode(node.left, depth + 1));}if (node.right != null) {queue.offer(new QueueNode(node.right, depth + 1));}}return 0;}
}
这段代码同样实现了计算二叉树最小深度的功能,但采用的是广度优先搜索(BFS)的方法,而非之前的深度优先搜索(DFS)。下面是代码的解释:
-
首先定义了一个内部类
QueueNode
,它包含两个成员变量:一个TreeNode node
用于存储树的节点,一个int depth
用于记录该节点到根节点的距离(深度)。 -
public int minDepth(TreeNode root)
方法接收一个 TreeNode 类型的参数root
,表示二叉树的根节点,返回值为最小深度。 -
如果根节点为空,返回 0,表示空树。
-
创建一个队列
Queue<QueueNode> queue
来进行广度优先搜索,并初始化一个QueueNode
对象,包含根节点及其深度 1,然后将此对象加入队列。 -
使用
while
循环处理队列直到其为空。在每次循环中:- 弹出队列头部的
QueueNode
,获取其中的节点node
和当前深度depth
。 - 如果当前节点
node
无左右子节点,即为叶子节点,直接返回当前深度depth
作为最小深度。 - 若当前节点有左子节点,创建一个新的
QueueNode
对象,包含左子节点和当前深度加 1,然后将其加入队列。 - 若当前节点有右子节点,创建一个新的
QueueNode
对象,包含右子节点和当前深度加 1,同样将其加入队列。
- 弹出队列头部的
-
当队列变空时,理论上不应该发生,因为只要有叶子节点就应该提前返回结果。但为了保持函数返回类型一致性,这里返回 0。在实际应用中,这个返回 0 的情况应该永远不会被执行到,因为一旦发现叶子节点就会立即返回其深度。
通过广度优先搜索,这个算法能够遍历每一层的节点,当遇到第一个没有子节点(即叶子节点)的节点时,就可以确定这是到根节点的最短路径,从而得到最小深度。
222. 完全二叉树的节点个数
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
class Solution {public int countNodes(TreeNode root) {if (root == null) {return 0;}int level = 0;TreeNode node = root;while (node.left != null) {level++;node = node.left;}int low = 1 << level, high = (1 << (level + 1)) - 1;while (low < high) {int mid = (high - low + 1) / 2 + low;if (exists(root, level, mid)) {low = mid;} else {high = mid - 1;}}return low;}public boolean exists(TreeNode root, int level, int k) {int bits = 1 << (level - 1);TreeNode node = root;while (node != null && bits > 0) {if ((bits & k) == 0) {node = node.left;} else {node = node.right;}bits >>= 1;}return node != null;}
}
这段代码是用来解决一个二叉树问题的,具体问题是计算完全二叉树中的节点个数。完全二叉树是每一层(除了可能的最后一层外)都完全填充的树,并且所有结点都尽可能地集中在左侧。代码采用了两种方法结合的策略:首先通过高度来定位最后一个非空节点所在的层数,然后在一个可能的范围内利用二分查找确定实际的节点数量。下面是详细的解释:
-
public int countNodes(TreeNode root)
方法是主要的接口,接收一个 TreeNode 类型的参数root
,表示二叉树的根节点,返回值为树中的节点总数。 -
首先,如果根节点为空,直接返回 0,因为空树没有节点。
-
接着,通过一个循环计算树的高度(层数),同时找到最后一层的第一个节点。
level
初始化为 0,node
初始化为root
,循环条件是node.left != null
,意味着只要当前节点有左子节点,就继续向左下移动并增加层数。 -
计算出
level
后,可以推断出这棵完全二叉树节点数量的大致范围:至少有low = 1 << level
节点(即2的level次方),最多有high = (1 << (level + 1)) - 1
节点。这里利用位运算快速计算2的幂次。 -
然后,在
low
和high
之间使用二分查找确定实际的节点数量。在循环中,首先计算中间值mid
,然后调用exists()
函数检查在这个位置上是否存在节点。根据exists()
的返回值调整查找范围:如果节点存在,则说明实际节点数至少为mid
,因此更新low = mid
;否则,节点不存在,说明实际节点数小于mid
,则更新high = mid - 1
。 -
当
low >= high
时,二分查找结束,返回low
作为完全二叉树的确切节点数。 -
public boolean exists(TreeNode root, int level, int k)
是一个辅助函数,用来判断在给定层级 (level
) 和位置 (k
) 是否存在节点。它模拟从根节点开始,根据二进制位确定向左还是向右走,逐步向下查找。bits
变量用于控制每一步的移动方向,初始值为1 << (level - 1)
,表示在当前层级上最左边的节点所对应的二进制位。随着循环的进行,bits
右移一位,直到为0,表示已经到达目标层级并完成查找。如果最终node
不为空,说明对应位置的节点存在,返回true
;否则,返回false
。
这种解法巧妙地结合了树的高度信息与二分查找的效率,可以在对数时间内解决问题,对于大规模的完全二叉树特别有效。
相关文章:

代码随想录-Day16
104. 二叉树的最大深度 方法一:深度优先搜索 class Solution {public int maxDepth(TreeNode root) {if (root null) {return 0;} else {int leftHeight maxDepth(root.left);int rightHeight maxDepth(root.right);return Math.max(leftHeight, rightHeight) …...

31.@Anonymous
1►@Anonymous原理 大家应该已经习惯我的教学套路,很多时候都是先使用,然后讲述原理。 上节课我们使用了注解@Anonymous,然后接口就可以直接被访问到了,不用token!不用token!不用token!。 我们一般知道,注解是给程序看的,给机器看的,当然也是给程序员看的。注解如果…...

oracle 表同一列只取最新一条数据写法
select * from (select t.*,row_number() over(partition by 去重列名 order by 排序列名 desc) as rnfrom 表名)where rn1 1.row_number() over(....): 为每条数据分配一个行号,1.2.3....这样的 2.partition by : 以某列作为分组,每个分组行号从1开始…...

C语言游戏实战(12):植物大战僵尸(坤版)
植物大战僵尸 前言: 本游戏使用C语言和easyx图形库编写,通过这个项目我们可以深度的掌握C语言的各种语言特性和高级开发技巧,以及锻炼我们独立的项目开发能力, 在开始编写代码之前,我们需要先了解一下游戏的基本规则…...

提权方式及原理汇总
一、Linux提权 1、SUID提权 SUID(设置用户ID)是赋予文件的一种权限,它会出现在文件拥有者权限的执行位上,具有这种权限的文件会在其执行时,使调用者暂时获得该文件拥有者的权限。 为可执行文件添加suid权限的目的是简…...

【leetcode----二叉树中的最大路径和】
二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二叉树的根节点 root ,…...

Rust: 编译过程中链接器 `cc` 没有找到
这个错误信息表明在编译过程中链接器 cc 没有找到。cc 通常是 C 编译器的符号链接,它指向系统上的实际 C 编译器,如 gcc 或 clang。这个错误通常意味着你的系统缺少必要的编译工具链。 要解决这个问题,你需要确保你的系统上安装了 C 编译器。…...

【vue-3】动态属性绑定v-bind
1、文本动态绑定: <input type"text" v-bind:value"web.url"> 简写: <input type"text" :value"web.url"> 2、文字样式动态绑定 <b :class"{textColor:web.fontStatus}">vue学…...

Rust:多线程环境下使用 Mutex<T> 还是 Arc<Mutex<T>> ?
在 Rust 中,Mutex 本身不是线程不安全的;它提供了内部的线程同步机制。然而,如果你想在多线程环境中共享同一个 Mutex,你需要确保这个 Mutex 可以被多个线程访问。为此,你通常需要使用 Arc<Mutex<T>>。Arc…...

关于如何创建一个可配置的 SpringBoot Web 项目的全局异常处理
前情概要 这个问题其实困扰了我一周时间,一周都在 Google 上旅游,我要如何动态的设置 RestControllerAdvice 里面的 basePackages 以及 baseClasses 的值呢?经过一周的时间寻求无果之后打算决定放弃的我终于找到了一些关键的线索。 当然在此…...

docker三种自定义网络(虚拟网络) overlay实现原理
docker提供了三种自定义网络驱动:bridge、overlay、macvlan。 bridge驱动类似默认的bridge网络模式。 overlay和macvlan是用于创建跨主机网络。 支持自定义网段、网关,docker network create --subnet 172.77.0.0/24 --gateway 172.77.0.1 my_n…...

C#上位机1ms级高精度定时任务
precisiontimer 安装扩展包 添加引用 完整代码 using PrecisionTiming;using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; us…...

盘点28个免费域名申请大全
盘点28个免费域名申请大全 免费域名推荐学习使用,免费就意味着没任何保障。 名称稳定时间支持解析模式后缀格式说明地址EU.org28 年NS.eu.org/. 国家简写.eu.org需要审核,稳定性高,限制少,国内访问有问题,可 CFeu.orgp…...

【vue】封装的天气展示卡片,在线获取天气信息
源码 <template><div class"sen_weather_wrapper"><div class"sen_top_box"><div class"sen_left_box"><div class"sen_top"><div class"sen_city">山东</div><qctc-time cl…...

【MySQL】库的操作和表的操作
库的操作和表的操作 一、库的操作1、创建数据库(create)2、字符集和校验规则(1)查看系统默认字符集以及校验规则(2)查看数据库支持的字符集(3)查看数据库支持的字符集校验规则(4)校验…...

【学习笔记】后端(Ⅰ)—— NodeJS(Ⅱ)
NodeJS 3、进阶篇 —— Express框架 3.1、Express 框架介绍 3.2、Express 框架初体验 3.3、使用 3.4、中间件 3.5、托管静态文件 3.6、获取表单数据 3.7、防盗链 3.8、路由模式化 3.8、EJS 模板引擎 3.9、express-generator…...

VMware报平台不支持虚拟化Win10家庭版关闭Hyper-V及内核隔离
1.BIOS中开启虚拟化功能 2.启动或关闭程序中找不到Hyper-v 停止 hypervisorlaunchtype(Windows Hyper-V 启动加载器) 以管理员的身份打开命令行窗口,运行如下命令,关闭停止 Windows Hyper-V 启动加载器 开启 Windows Hyper-V 启…...

简单介绍十款可以免费使用的API测试工具
API开发应该是后端开发最常见的工作,而调试和测试API是非常关键的,这篇文章简单介绍几款常用的工具以供大家参考。 SoapUI SoapUI是很老牌的工具的,在之前Webservice盛行的时候经常会用到。 现在官方推出了Pro版本的ReadyAPI,但要…...

非授权人员进入报警系统
非授权人员进入报警系统基于智能视频分析技术和深度学习技术,非授权人员进入报警系统通过现场已经装好的监控摄像头针对人体进行精准检测,并根据设置的禁入区范围进行判断。通过图像处理和人体识别算法,非授权人员进入报警系统可以在实时监测…...

Mysql基础教程(03):AND
MySQL AND 运算符的用法 本文介绍了 MySQL 中如何在 WHERE 子句中使用 AND 运算符组合多个查询条件过滤查询数据。 当使用 SELECT 查询数据时,如果 WHERE 子句中有多个条件,可以根据需要使用 AND, OR, 或者 NOT 运算符将他们组合起来。本文主要介绍 AN…...

为什么要使用 eval
调用 eval 方法的原因是为了确保模型在进行预测时使用正确的配置。在训练过程中,某些层(如 Dropout 层)的行为是为了正则化而设计的,它们会在每次迭代中随机丢弃一些神经元的输出。而在评估模式下,这些层将不再随机丢弃…...

BCD编码(8421)介绍
概念 BCD (Binary-Coded Decimal) 是一种二进制的数字编码形式,其特点每个十进制数位用4个二进制位来表示。 在网络IO中,你传输一个数字类型最少需要一字节,传输两个数字类型最少需要两字节,但是当你使用BCD编码后传输ÿ…...

前端javascript包管理,npm升级用pnpm
一 pnpm 介绍 pnpm(Package Manager)是一个快速、节省磁盘空间的 JavaScript 包管理器,它是 Node.js 生态系统中 npm 的一个替代品。pnpm 解决了传统包管理工具在处理依赖时的一些痛点,特别是关于存储空间使用和依赖地狱的问题。…...

数据库操作(函数)
函数是一段可以直接被另外一段程序调用的程序或代码 一。字符串函数 1.concat(s1,s1....sn):字符串拼接,将s1,s2,sn拼接为一个字符串 例如: select concat("hello","world"); 2.lower(str&…...

[建堆堆排序的时间复杂度推导]向上建堆向下建堆堆排序的时间复杂度分析推导
💖💖💖欢迎来到我的博客,我是anmory💖💖💖 又和大家见面了 欢迎来到动画详解数据结构系列 作为一个程序员你不能不掌握的知识 先来自我推荐一波 个人网站欢迎访问以及捐款 推荐阅读 如何低成本搭…...

【C++初阶】--- C++入门(上)
目录 一、C的背景及简要介绍1.1 什么是C1.2 C发展史1.3 C的重要性 二、C关键字三、命名空间2.1 命名空间定义2.2 命名空间使用 四、C输入 & 输出 一、C的背景及简要介绍 1.1 什么是C C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题&…...

安装和使用图像处理软件GraphicsMagick @FreeBSD
GraphicsMagick是一个用于处理图像的读取、写入和操作的工具软件。它被誉为图像处理领域的“瑞士军刀”,短小精悍,支持超过88种图像格式,包括DPX、GIF、JPEG、JPEG-2000、PNG、PDF、PNM和TIFF等。 GraphicsMagick的主要特点包括:…...

一款功能强大的安卓虚拟机应用——VMOS Pro使用分享
前段时间我刚刚分享一个WeChat平板模块能够允许用户自由修改系统设置,让你的Android备用手机焕发新生,实现手机PAD化,实现两台设备同时登录微信号。今天我分享的这个相比WeChat更为简单,因为它可以通过虚拟机的方式进行多种androi…...

【408真题】2009-12
“接”是针对题目进行必要的分析,比较简略; “化”是对题目中所涉及到的知识点进行详细解释; “发”是对此题型的解题套路总结,并结合历年真题或者典型例题进行运用。 涉及到的知识全部来源于王道各科教材(2025版&…...

vue3第三十三节(TS 之 computed watch)
vue3 组合是API 中我们经常使用的 监听函数 computed 和 watch使用 1、computed 里面添加类型 <script setup lang"ts"> import { ref, computed } from vue const age ref(18) // 定义一个Person 接口 interface Person {age: numbername: string } const…...