【算法通关村 Day6】二叉树层次遍历
树与层次遍历青铜挑战
理解树的结构
通过中序和后序遍历序列恢复二叉树是一个经典的二叉树构建问题。给定二叉树的中序遍历序列和后序遍历序列,我们可以利用以下步骤进行恢复。
思路:
- 后序遍历的特点:
- 后序遍历的最后一个节点是树的根节点。
- 中序遍历的特点:
- 中序遍历中,根节点左边的元素是左子树,根节点右边的元素是右子树。
步骤:
- 从后序遍历的最后一个节点开始,它是树的根节点。
- 在中序遍历中找到该根节点的位置,它把中序遍历序列分成两部分:左子树和右子树。
- 递归地对左子树和右子树的中序遍历和后序遍历进行相同的操作,直到所有节点都被处理。
import java.util.HashMap;
import java.util.Map;public class BinaryTreeBuilder {// 定义二叉树节点结构static class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int val) {this.val = val;}}// 用于存储中序遍历的节点索引,方便快速查找private Map<Integer, Integer> inorderMap;// 主方法,传入中序和后序序列,建立二叉树 public TreeNode buildTree(int[] inorder, int[] postorder) {inorderMap = new HashMap<>();// 将中序遍历的节点及其对应索引存入mapfor(int i = 0; i < inorder.length; i++){inorderMap.put(inorder[i], i);}// 从后序数组的最后一个元素开始,递归构建树return build(inorder, postorder, 0, inorder.length - 1, new int[]{postorder.length - 1});}// 递归构建树private TreeNode build(int[] inorder, int[] postorder, int inStart, int inEnd, int[] postIndex){// 递归终止条件:没有元素可处理if (inStart > inEnd) {return null;}// 确保postIndex不会越界if (postIndex[0] < 0) {return null;}//后序遍历的当前节点int rootVal = postorder[postIndex[0]--];TreeNode root = new TreeNode(rootVal);// 获取当前根节点在中序遍历中的位置int rootIndex = inorderMap.get(rootVal);// 先构建右子树,再构建左子树(因为postorder是后序遍历)root.right = build(inorder, postorder, rootIndex + 1, inEnd, postIndex);root.left = build(inorder, postorder, inStart, rootIndex - 1, postIndex);return root;}// 辅助方法:打印二叉树(中序遍历)public void inorderTraversal(TreeNode root){if(root != null){inorderTraversal(root.left);System.out.print(root.val + " ");inorderTraversal(root.right);}}public static void main(String[] args) {BinaryTreeBuilder builder = new BinaryTreeBuilder();//示例int[] inorder = {9, 3, 15, 20, 7};int[] postorder = {9, 15, 7, 20, 3};// 构建二叉树TreeNode root = builder.buildTree(inorder, postorder);//打印构建的二叉树的中序遍历作验证System.out.println("Inorder Traversal: ");builder.inorderTraversal(root);}
}
-
后序遍历:后序遍历的最后一个元素是
3,所以树的根节点是3。 -
中序遍历:在中序遍历数组中,
3位于索引位置1,这意味着我们可以将树分为左右子树:- 左子树:中序序列
{9}(在3左边) - 右子树:中序序列
{15, 20, 7}(在3右边)
- 左子树:中序序列
-
递归构建左子树和右子树:
- 左子树:对应的后序序列是
{9}。所以左子树的根节点就是9,没有子节点(因为没有更多元素)。 - 右子树:右子树的中序序列是
{15, 20, 7},后序序列是{15, 7, 20}。我们从后序序列中取出20作为右子树的根节点。然后将其继续分为左右子树:- 左子树:中序序列
{15}和后序序列{15},所以左子树的根节点就是15。 - 右子树:中序序列
{7}和后序序列{7},所以右子树的根节点就是7。
- 左子树:中序序列
- 左子树:对应的后序序列是
构建出的树结构:
3/ \9 20/ \15 7
树与层次遍历白银挑战
二叉树层次遍历的经典问题
二叉树层次遍历
二叉树的层次遍历(也叫广度优先遍历,BFS)是指从根节点开始,逐层遍历树的节点,每一层从左到右依次访问。
在 Java 中实现二叉树的层次遍历可以使用队列(Queue)来辅助。队列是一种先进先出的数据结构,适合用来逐层遍历树的节点。
假设你已经有了二叉树的定义(TreeNode 类),可以按以下方式实现层次遍历:
import java.util.LinkedList;
import java.util.Queue;class TreeNode {int val;TreeNode left, right;TreeNode(int val) {this.val = val;left = right = null;}
}public class BinaryTreeLevelOrderTraversal {public void levelOrder(TreeNode root) {if (root == null) {return;}Queue<TreeNode> queue = new LinkedList<>();queue.offer(root); // 将根节点放入队列while (!queue.isEmpty()) {TreeNode currentNode = queue.poll(); // 取出队首元素// 访问当前节点System.out.println(currentNode.val + " ");// 如果左子节点不为空,将其加入队列if (currentNode.left != null) {queue.offer(currentNode.left);} // 如果右子节点不为空,将其加入队列if (currentNode.right != null) {queue.offer(currentNode.right);}}}public static void main(String[] args) {BinaryTreeLevelOrderTraversal tree = new BinaryTreeLevelOrderTraversal();// 创建一个示例二叉树TreeNode root = new TreeNode(1);root.left = new TreeNode(2);root.right = new TreeNode(3);root.left.left = new TreeNode(4);root.left.right = new TreeNode(5);root.right.left = new TreeNode(6);root.right.right = new TreeNode(7);// 执行层次遍历System.out.print("Level Order Traversal: ");tree.levelOrder(root);}
}
- TreeNode 类:代表二叉树的节点,每个节点有一个整数值(
val),以及指向左右子节点的指针(left和right)。 - levelOrder 函数:这个方法实现了层次遍历。它使用一个队列来逐层遍历树的节点。根节点首先入队,然后每次从队列中取出一个节点,访问它的值,并将它的左右子节点加入队列,直到队列为空。
- 主函数(main):创建一个示例二叉树并调用
levelOrder函数进行层次遍历。
- 时间复杂度:O(n),其中 n 是树中的节点数。每个节点都只会被访问一次。
- 空间复杂度:O(n),最坏情况下,队列中最多会保存树的最大宽度的节点数(即叶子节点的数目)。
处理不同层的值
例题一 在每个树行中找最大值
给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。
leetcode515

你可以通过广度优先搜索 (BFS) 来遍历二叉树的每一层,然后找出每一层的最大值。具体做法是使用一个队列存储每一层的节点,遍历完一层后找出该层的最大值。
下面是使用 Java 实现的代码:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;}
}public class LargestValues {public List<Integer> largestValues(TreeNode root){List<Integer> result = new ArrayList<>();if (root == null) {return result;}Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {int levelSize = queue.size();// 初始化当前层最大值为最小整数int maxVal = Integer.MIN_VALUE;for(int i = 0; i < levelSize; i++) {TreeNode currentNode = queue.poll();// 更新当前层最大值maxVal = Math.max(maxVal, currentNode.val);if (currentNode.left != null) {queue.offer(currentNode.left);}if (currentNode.right != null) {queue.offer(currentNode.right);}}result.add(maxVal);}return result;}public static void main(String[] args) {TreeNode root = new TreeNode(1);root.left = new TreeNode(3);root.right = new TreeNode(2);root.left.left = new TreeNode(5);root.left.right = new TreeNode(3);root.right.right = new TreeNode(9);LargestValues solution = new LargestValues();List<Integer> result = solution.largestValues(root);System.out.println(result); // Output: [1, 3, 9]}
}
时间复杂度:
- O(n),其中 n 是二叉树的节点数。每个节点都被访问一次。
空间复杂度:
- O(m),其中 m 是二叉树的最大宽度。最坏情况下,队列存储的是树的最大宽度的节点。
例题二 二叉树的层平均值
给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。
leetcode637

解法:
同样可以使用层序遍历(BFS)来解这个问题,逐层计算每一层的节点平均值。我们可以使用一个队列来帮助实现层序遍历。对于每一层,计算所有节点的和,并除以该层节点的个数,最后返回结果。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;}
}public class AverageOfLevels {public List<Double> averageOfLevels(TreeNode root){List<Double> result = new ArrayList<>();if (root == null) {return result;}Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {int levelSize = queue.size();double levelSum = 0;for(int i = 0; i < levelSize; i++){TreeNode node = queue.poll();levelSum += node.val;if (node.left != null) {queue.offer(node.left);}if (node.right != null) {queue.offer(node.right);}}result.add(levelSum / levelSize);}return result;}
}
例题三 二叉树的右视图
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
leetcode199

-
BFS遍历:我们使用一个队列
queue来进行广度优先遍历。每次取出一层的节点,然后依次访问这一层的所有节点。 -
每层最右节点:在遍历一层时,我们只关注该层的最右边的节点,因此在每次循环结束时,我们记录当前层的最后一个节点的值(即右侧可见的节点)。
-
更新队列:每访问一个节点时,将它的左右子节点(如果有的话)加入队列,这样可以确保后续的层次遍历。
-
返回结果:最终,
result列表中存储的就是每一层的最右边的节点值,这就是从右侧所能看到的节点值。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;}
}public class RightSideView {public List<Integer> rightSideView(TreeNode root) {List<Integer> result = new ArrayList<>();if (root == null) {return result;}Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {int size = queue.size();TreeNode rightMostNode = null;for(int i = 0; i < size; i++){TreeNode currentNode = queue.poll();rightMostNode = currentNode;if (currentNode.left != null) {queue.offer(currentNode.left);}if (currentNode.right != null) {queue.offer(currentNode.right);}}result.add(rightMostNode.val);}return result;}
}
- 时间复杂度是 O(n),其中
n是二叉树中的节点数。我们需要遍历每一个节点。 - 空间复杂度是 O(m),其中
m是二叉树的最大层宽度(即队列中最多存储的节点数)。
例题四 找树左下角的值
给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。
leetcode513

解法:
- 层序遍历:队列中保存着当前正在遍历的节点。对于每一层的节点,我们需要记录该层的第一个节点,这就是我们所需的“最左边的节点”。
leftmostValue:记录每层第一个节点的值。每次进入新的一层,i == 0时会更新该变量为当前层的第一个节点的值。- 队列中的操作:从左到右遍历当前层的节点,每次加入左子节点和右子节点到队列中。
import java.util.LinkedList;
import java.util.Queue;class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;}
}public class FindBottomLeftValue {public int findBottomLeftValue(TreeNode root){if (root == null) {return -1;}Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);int leftmostValue = 0;while (!queue.isEmpty()) {int levelSize = queue.size();for (int i = 0; i < levelSize; i++) {TreeNode currentNode = queue.poll();if (i == 0) {leftmostValue = currentNode.val;}if (currentNode.left != null) {queue.offer(currentNode.left);}if (currentNode.right != null) {queue.offer(currentNode.right);}}}return leftmostValue;}
}
- 时间复杂度仍然是 O(N),因为每个节点都会被访问一次。
- 空间复杂度是 O(N),因为在最坏的情况下,队列需要存储二叉树的最后一层节点。
相关文章:
【算法通关村 Day6】二叉树层次遍历
树与层次遍历青铜挑战 理解树的结构 通过中序和后序遍历序列恢复二叉树是一个经典的二叉树构建问题。给定二叉树的中序遍历序列和后序遍历序列,我们可以利用以下步骤进行恢复。 思路: 后序遍历的特点: 后序遍历的最后一个节点是树的根节点…...
安全面试2
文章目录 简单描述一下什么是水平越权,什么是垂直越权,我要发现这两类漏洞,那我代码审计要注意什么地方水平越权:垂直越权:水平越权漏洞的审计重点垂直越权漏洞的审计重点 解释一下ssrf漏洞原理攻击场景修复方法 横向移…...
【JavaScript进阶】构造函数数据常用函数
目录 本章节用到的所有素材都可以找到:素材自取~~~~ 1、深入对象 1.1创建对象三种方式 1.2 构造函数 练习 利用构造函数创建多个对象 实例化执行过程 1.3实例成员&静态成员 2. 内置构造函数 2.1 Object 2.2 Array 练习 员工涨薪计算成本 2.3 St…...
在PiscTrace开发者版上直接处理图像色阶分布
在图像处理和计算机视觉中,色阶分布(或称灰度分布)是描述图像中像素强度分布的一个重要概念。它对于理解图像的亮度、对比度、纹理和细节等方面具有关键作用。通过色阶分布的分析,我们能够获得图像的整体信息,从而帮助…...
趣味数学300题1981版-十五个正方形
分析:移动两根变成11个正方形很简单: 移动4根变成15个正方形,分析: 一个田字格包含5个正方形,若要15个正方形需要3个田字格,如果3个田字格完全不重合,需要6*318根火柴。如果合并正方形的边&…...
Selenium实战案例1:论文pdf自动下载
在上一篇文章中,我们介绍了Selenium的基础用法和一些常见技巧。今天,我们将通过中国科学:信息科学网站内当前目录论文下载这一实战案例来进一步展示Selenium的web自动化流程。 目录 中国科学:信息科学当期目录论文下载 1.网页内…...
前端面试-JavaScript 数据类型检测全解
目录 一、基础检测方法 二、方法深度解析 1. typeof 运算符 2. instanceof 运算符 3. 终极检测方案 三、特殊场景检测方案 四、手写实现原理 1. 通用类型检测函数 2. 改进版数组检测(兼容旧浏览器) 五、常见面试陷阱 六、最佳实践指南 七、扩…...
nginx 反向代理 配置请求路由
nginx | 反向代理 | 配置请求路由 nginx简介 Nginx(发音为“Engine-X”)是一款高性能、开源的 Web 服务器和反向代理服务器,同时也支持邮件代理和负载均衡等功能。它由俄罗斯程序员伊戈尔西索夫(Igor Sysoev)于 2004…...
用户中心项目教程(十)---注册里面的重定向排查和相关的修改
文章目录 1.注册逻辑的设计和实现2.解决自带的这个重定向的问题3.增加属性的相关操作4.关于如何修改页面上面的绿色按钮 1.注册逻辑的设计和实现 上次说到了的是登录功能,我们使用数据库里面存在的这个存在的账户和密码进行登录,但是是无法进行跳转的&a…...
根据音频中的不同讲述人声音进行分离音频 | 基于ai的说话人声音分离项目
0.研究背景 在实际的开发中可能会遇到这样的问题,老板让你把音频中的每个讲话人的声音分离成不同的音频片段。你可以使用au等专业的音频处理软件手动分离。但是这样效率太慢了,现在ai这么发达,我们能否借助ai之力来分离一条音频中的不同的说…...
【单片机】【UDS】 (单帧与多帧) 数据传输
对于使用 CAN 的诊断通信系统,每个单帧 (SF)、 第一帧 (FF)、 连续帧 (CF) 或流控 制帧 (FC) 有 8 字节数据场;其中单帧的 CAN_DL≤8 且第一帧的 FF_DL≤4095;下表 中已定义 每个报文的类型。 CAN FD 帧的数据场支持最大 64 个字节࿰…...
WebXR教学 02 配置开发环境
默认操作系统为Windows 1.VS Code VS Code 是一款轻量级、功能强大的代码编辑器,适用于多种编程语言。 下载 步骤 1:访问 VS Code 官方网站 打开浏览器(如 Chrome、Edge 等)。 在地址栏输入以下网址: https://code.v…...
MySql数据库运维学习笔记
数据库运维常识 DQL、DML、DCL 和 DDL 是 SQL(结构化查询语言)中的四个重要类别,它们分别用于不同类型的数据库操作,下面为你简单明了地解释这四类语句: 1. DQL(数据查询语言,Data Query Langu…...
网络协议相关问题
1. HTTP 与 HTTPS 的区别 HTTP:明文传输,端口80,无加密,易被窃听或篡改。HTTPS:SSL/TLS加密传输,端口443,通过数字证书验证身份,防止中间人攻击。 混合加密:非对称加密交…...
宇树科技13家核心零部件供应商梳理!
2025年2月6日,摩根士丹利(Morgan Stanley)发布最新人形机器人研报:Humanoid 100: Mapping the Humanoid Robot Value Chain(人形机器人100:全球人形机器人产业链梳理)。 Humanoid 100清单清单中…...
Windows 启动 SSH 服务报错 1067
Windows 启动 SSH 服务报错 1067 一、原本安装的 Windows 自带的 SSH 服务 按 Windows 键 -> 设置 -> 系统 -> 可选功能 在 添加的功能 查看是否安装了 OpenSSH 服务 一开始 执行 net start sshd 是可以正常启动的 并且其他机器也可以通过 ssh 访问 这个电脑 但是有…...
kkFileView报错no office manager available
背景 部署环境:虚机Linux系统 发生问题的版本:4.1.0-SNAPSHOT 现象:有的docx文件可以预览,有的不可以。不可以的就怎么打开都不可以(不管你是躺着,站着,坐着,睡着,趴着都不行,哈哈) 报错内容 贴出主要的报错内容步骤: > no office manager available > tr…...
ARMS 助力假面科技研发运维提效,保障极致游戏体验
客户介绍与项目背景 假面科技成立于 2014 年,致力于打造创新的数字产品,火爆一时的“狼人杀”、“谁是卧底”、“足记相机”都是假面科技旗下产品,公司产品总数超过 40 款,覆盖用户数超过 2 亿人。 随着业务的持续发展ÿ…...
趣味数学300题1981版-八个等式、五个5等于24
八个等式 分析:此问题的求解思路是按照最后一步运算的运算符号进行分类。示例中最后一步的运算是除法,只要被除数与除数相等且不为0,就可以得到结果1.因此我们还可以对于结果等于1的情况列出其他的算式。如果保持最后一步运算为除法运算&…...
关闭超时订单和七天自动确认收货+RabbitMQ规范
关闭超时订单 创建订单之后的一段时间内未完成支付而关闭订单的操作,该功能一般要求每笔订单的超时时间是一致的 TTL(Time To Live)存活时间,只能被设置为某个固定的值,不能更改,否则抛出异常 死信&#…...
DDD领域驱动开发第2讲:领域驱动开发在货代订单业务的实践
领域驱动开发在货代订单业务的实践 本文是DDD领域驱动开发第2讲,先讲解当前业务存在哪些问题,什么是DDD,为啥需要使用DDD解决现有业务问题,DDD让技术主动理解业务,通过领域模型将可以描述各个业务领域之间的关系,最后讲解领域驱动开发在货代订单的实践。 文章目录 领域驱…...
【Qt学习】| 如何使用QVariant存储自定义类型
QVariant是Qt框架中的一个通用数据类型,可以存储多种类型的数据,主要作用是提供一种类型安全的方式来存储和传递不同类型的数据,而不需要显示地指定数据类型。 QVariant提供了诸多构造函数可以非常方便地对基础数据类型(如&#x…...
分割 学习笔记cvpr2024
目录 LiteMedSam 模型37m LightM-Unet 500 str 依赖项: MLWnet 73 star memsam 340M 126 star LiteMedSam 模型37m https://github.com/bowang-lab/MedSAM/blob/LiteMedSAM/README.md LightM-Unet 500 str https://github.com/MrBlankness/LightM-UNet/blob model = Li…...
【多模态处理篇一】【 深度解析DeepSeek图文匹配:CLIP模型迁移实战——从原理到落地的保姆级教程】
引言:当CLIP遇到DeepSeek,会发生什么化学反应? 如果说CLIP是OpenAI为多模态领域投下的"原子弹",那DeepSeek的迁移实战方案就是给这颗原子弹装上了精确制导系统。这个组合能让你用一张猫咪表情包搜到全网同类梗图,还能让电商平台自动生成百万级商品描述,甚至帮…...
水果生鲜农产品推荐系统 协同过滤余弦函数推荐水果生鲜农产品 Springboot Vue Element-UI前后端分离 代码+开发文档+视频教程
水果生鲜农产品推荐系统 协同过滤余弦函数推荐水果生鲜农产品 Springboot Vue Element-UI前后端分离 【亮点功能】 1.SpringbootVueElement-UIMysql前后端分离 2.Echarts图表统计数据, 直观展示数据情况 3.发表评论后,用户可以回复评论, 回复的评论可以被再次回复, …...
1.vue使用vite构建初始化项目
npm create vuelatest❯ npm create vuelatest> npx > create-vueVue.js - The Progressive JavaScript Framework✔ Project name: … vue3_test ✔ Add TypeScript? … No / Yes ✔ Add JSX Support? … No / Yes ✔ Add Vue Router for Single Page Application dev…...
在PyCharm中运行Jupyter Notebook的.ipynb文件及其pycharm软件的基础使用
(注意需使用PyCharm专业版,学生、教师可以申请免费使用:https://www.jetbrains.com/shop/eform/students) 1. pycharm2024版汉化 https://blog.csdn.net/m0_74103046/article/details/144560999 2. pycharm中的python控制台和J…...
深度体验通义灵码2.0 AI 程序员
通义灵码2.0 作为一名开发者,我去年就使用过1.0,近期有幸体验了 2.0,这是一款集成了 Deepseek 大模型的智能编码助手。在这次体验中,我深入探索了新功能开发、跨语言编程、单元测试自动生成、图生代码等多个场景,深刻…...
Coroutine协程
cooperation 协作 routine 程序,常规 协程核心:函数能够被挂起suspend,当然也能被回复resume 内置函数:also 返回对象本身 扩展: 内置函数let、also、with、run、apply大大提高你的开发效率! 协程的作用:…...
使用IDEA提交SpringBoot项目到Gitee上
登录Gitee并新建仓库 创建本地仓库 提交本地代码到本地仓库 提交本地代码到远程仓库...
