代码随想录-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…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...

实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...

Linux操作系统共享Windows操作系统的文件
目录 一、共享文件 二、挂载 一、共享文件 点击虚拟机选项-设置 点击选项,设置文件夹共享为总是启用,点击添加,可添加需要共享的文件夹 查询是否共享成功 ls /mnt/hgfs 如果显示Download(这是我共享的文件夹)&…...
简单介绍C++中 string与wstring
在C中,string和wstring是两种用于处理不同字符编码的字符串类型,分别基于char和wchar_t字符类型。以下是它们的详细说明和对比: 1. 基础定义 string 类型:std::string 字符类型:char(通常为8位)…...

Redis专题-实战篇一-基于Session和Redis实现登录业务
GitHub项目地址:https://github.com/whltaoin/redisLearningProject_hm-dianping 基于Session实现登录业务功能提交版本码:e34399f 基于Redis实现登录业务提交版本码:60bf740 一、导入黑马点评后端项目 项目架构图 1. 前期阶段2. 后续阶段导…...