java算法day11
- 二叉树的递归遍历
- 二叉树的非递归遍历写法
- 层序遍历
递归怎么写?
按照三要素可以保证写出正确的递归算法:
1.确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
2.确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
3.确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
前置必会
一定要会自己写出树的类定义
public class TreeNode{TreeNode left;TreeNode right;int val;TreeNode(){}TreeNode(int val){this.val = val}TreeNode(int val,TreeNode right,TreeNode left){this.val = val;this.left = left;this.right = right;
}
144.二叉树的前序遍历
递归写法
class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();dfs(root,result);return result;}void dfs(TreeNode root,List<Integer> result){if(root==null){return;}result.add(root.val);dfs(root.left,result);dfs(root.right,result);}}
非递归写法:
我认为就是记思路。
1.用一个栈作为辅助。
2.前序遍历是根左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。之所以是反过来是因为,这样出栈的时候才是根左右的顺序。直接来看图模拟
即每次在处理根节点的时候,将根节点出队,然后将其右孩子先进栈,再将左孩子进栈。
这样进行处理之后,出栈的结果就会是前序遍历的结果。
如果还是不懂,我建议直接结合代码,然后结合上面图,记下来这个做法。我觉得这个直接想是不好想的。
非递归其实就是用辅助数据结构,配合循环
class Solution {public List<Integer> preorderTraversal(TreeNode root) {//结果集List<Integer> result = new ArrayList<>();//特判if(root == null){return result;}//创建辅助栈Deque<TreeNode> st = new ArrayDeque<>();//根节点先入栈st.push(root);//当栈不空的时候while(!st.isEmpty()){//先处理根节点,即将根节点出栈。这就对应着根TreeNode node = st.pop();//将出栈的根节点加入结果集result.add(node.val);//先将右节点加入栈中,可以这么想,早点加入,那么就晚点出。所以右节点出的时候,要比左节点晚,那么这里出也就是和上面节点出栈一个道理。所以这里就完成了根左右里面的根左。因为左节点进的晚,出的就早。if(node.right!=null){st.push(node.right);}//然后才是到左节点,晚点进就可以早点出。if(node.left!=null){st.push(node.left);}}return result;}
}
这是我第二写总结的流程:
1.初始化:
创建一个空的结果列表
创建一个辅助栈
将根节点压入栈中
2.主循环: 当栈不为空时,执行以下操作:
a. 处理当前节点:
从栈中弹出一个节点
将该节点的值添加到结果列表中
b. 压入子节点:
如果该节点有右子节点,将右子节点压入栈中
如果该节点有左子节点,将左子节点压入栈中
返回结果列表
这种方法的关键点在于:
根节点最先被处理,这符合前序遍历的"根-左-右"顺序。
右子节点先于左子节点入栈,这确保了左子节点会先于右子节点被处理。
这种非递归方法的优点是:
避免了递归调用的开销
对于深度很大的树,可以防止栈溢出
实现简单,易于理解
与中序遍历的非递归方法相比,前序遍历的非递归实现更为直观,因为它可以直接按照遍历顺序处理节点,而不需要像中序遍历那样先到达最左节点。
145.二叉树的后序遍历
递归写法
class Solution {public List<Integer> postorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();dfs(root,result);return result;}public void dfs(TreeNode root,List<Integer> result){if(root==null){return;}dfs(root.left,result);dfs(root.right,result);result.add(root.val);}
}
非递归写法:
这个解法是一个技巧解。
由于前序遍历是根左右,而后续遍历是左右根。所以如果调整一下前序遍历的顺序,先加左节点,再加右节点,那么得到的结果就是按根右左规则得到的。所以此时做翻转,那么得到的结果就是按左右根得到的结果。与后序遍历的结果不谋而合。
所以解法就是线序遍历调整左右入栈方式,然后再对最终结果做翻转。
class Solution {public List<Integer> postorderTraversal(TreeNode root) {Deque<TreeNode> st = new ArrayDeque<>();List<Integer> result = new ArrayList<>();if(root==null){return result;}st.push(root);while(!st.isEmpty()){TreeNode node = st.pop();result.add(node.val);if(node.left!=null){st.push(node.left);}if(node.right!=null){st.push(node.right);}}Collections.reverse(result);return result;}
}
94.二叉树的中序遍历
递归写法
class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();dfs(root,result);return result;}public void dfs(TreeNode root,List<Integer> result){if(root==null){return;}dfs(root.left,result);result.add(root.val);dfs(root.right,result);}
}
非递归写法:
看这个图,然后结合图片,记下这个做法:
1.初始化:
创建一个空的结果列表和一个空的栈
将当前节点设置为根节点
2.主循环: 当当前节点不为空或栈不为空时,执行以下操作:
a. 左子树遍历:
如果当前节点不为空
将当前节点压入栈
移动到左子节点
b. 节点处理:
如果当前节点为空(即已经到达最左端)
从栈中弹出一个节点
将该节点的值添加到结果列表中
将当前节点移动到右子节点
3.返回结果列表
这种方法模拟了递归的过程:
首先尽可能深入地遍历左子树
然后处理节点本身
最后遍历右子树
使用栈来保存已经遍历过的父节点,以便在处理完左子树后能够回溯。
还是按代码思维走一遍,又和自己想的还是有一点区别。
class Solution {public List<Integer> inorderTraversal(TreeNode root) {//结果集List<Integer> result = new ArrayList<>();//特判if(root==null){return result;}//辅助栈Deque<TreeNode> st = new ArrayDeque<>();//用一个遍历指针指向rootTreeNode cur = root;//这里就是进递归处理的逻辑,循环条件决定了能不能递归处理下去。//cur!=null表明这个元素不空,可以进栈,!st.isEmpty(),是用来回溯的,表明栈中还有走过的元素需要进行处理。所以栈中走过的元素没处理完也要继续处理。while(cur!=null || !st.isEmpty()){//根据中序,左根右的走法,需要一路向最左下走。走的过程就一直入栈,保存之前的状态。如果cur==null,说明走到最左下的节点的左分支上了,也就是null。if(cur!=null){st.push(cur);cur = cur.left;}else{//不能继续往左走了才处理这里,这里就是开始回溯,回溯一下回到最左下的节点,此节点并不是null。那就把这个元素出栈,把这个元素的val加入结果集。由于每个元素都要左根右,这里处理了根节点,那还要往右节点走。下一波显然cur还是null,那么此时就会弹出沿着路线返回的根节点,往后都是这样。注意是依靠弹出的节点进行转移,因为栈里面的节点才是记录先前的状态,别自己瞎写一个。cur = st.pop();result.add(cur.val);cur = cur.right;}}//当st里面为空的时候,就是所有节点都处理完的时候。return result;}
}
102.二叉树的层序遍历
用队列。
看一个图就懂了。
队列先进先出,符合一层一层遍历的逻辑。
下面的代码就是二叉树层序遍历的模板题,会这个模板,之后遇到只要是层序遍历的题,随便杀。
总的来说,这个解法看完,里面的len的处理我是当时没想到的。
class Solution {public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> result = new ArrayList<>();checkFun(root,result);return result;}public void checkFun(TreeNode node,List<List<Integer>> result){//特判if(node==null){return;}//辅助队列Deque<TreeNode> que = new ArrayDeque<>();//根节点先入队que.offerLast(node);//队列不空就代表流程还没有结束while(!que.isEmpty()){//记录一层元素的结果集List<Integer> itemList = new ArrayList<Integer>();//在一开始先记录长度,该长度即队列目前的长度,就是该层元素的个数。//记录len就是为了做一个界限,即处理完这一层元素后,就要收集一次结果集。int len = que.size();//这个循环做依次将该层元素出队,并扩展其左右节点加入队列当中。while(len>0){//先出队TreeNode tmpNode = que.pollFirst();//加入结果集itemList.add(tmpNode.val);//扩展左右节点,加入队列中。if(tmpNode.left!=null){que.offerLast(tmpNode.left);}if(tmpNode.right!=null){que.offerLast(tmpNode.right);}//做完之后该层元素数量要-1len--;}//处理完一层后记录一次结果。result.add(itemList);}}
}
107.二叉树的层序遍历Ⅱ
在上题的基础上将结果集翻转就结束了。
class Solution {public List<List<Integer>> levelOrderBottom(TreeNode root) {List<List<Integer>> result = new ArrayList<>();checkFun(root,result);Collections.reverse(result);return result;}public void checkFun(TreeNode node,List<List<Integer>> result){if(node==null){return;}Deque<TreeNode> que = new ArrayDeque<>();que.offerLast(node);while(!que.isEmpty()){List<Integer> itemList = new ArrayList<Integer>();int len = que.size();while(len>0){TreeNode tmpNode = que.pollFirst();itemList.add(tmpNode.val);if(tmpNode.left!=null){que.offerLast(tmpNode.left);}if(tmpNode.right!=null){que.offerLast(tmpNode.right);}len--;}result.add(itemList);}}
}
相关文章:

java算法day11
二叉树的递归遍历二叉树的非递归遍历写法层序遍历 递归怎么写? 按照三要素可以保证写出正确的递归算法: 1.确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且…...

linux下安装cutecom串口助手;centos安装cutecom串口助手;rpm安装包安装cutecom串口助手
在支持apt-get的系统下安装 在终端命令行中输入: sudo apt-get install cutecom 安装好后输入 sudo cutecom 就可以了 关于如何使用,可以看这个https://www.cnblogs.com/xingboy/p/14388610.html 如果你的电脑不支持apt-get。 那我们就通过安装包…...

2024年信息系统项目管理师2批次上午客观题参考答案及解析(1)
1、关于收集需求管理过程及相关技术的描述,正确的是() A.需求跟踪矩阵是把产品需求从其来源链接到能满足需求的可交付成果的一种表格 B.原型法是一种结构化的头脑风暴形式,通过投票排列最有用的创意 C&am…...

Xinstall揭秘:APP推广数据背后的真相,让你的营销更精准!
在这个移动互联网时代,APP如同雨后春笋般涌现,但如何在这片红海中脱颖而出,成为每一个开发者与运营者面临的共同难题。其中,APP推广统计作为衡量营销效果、优化推广策略的关键环节,更是不可忽视的一环。今天࿰…...

科研绘图系列:R语言小提琴图(Violin Plot)
介绍 小提琴图(Violin Plot)是一种结合了箱线图和密度图的图表,它能够展示数据的分布密度和分布形状。以下是对小提琴图的详细解释: 小提琴图能表达: 数据分布:小提琴图通过在箱线图的两侧绘制曲线来展示数据的分布密度,曲线的宽度表示数据点的密度。集中趋势:箱线图部…...
【Vite】修改构建后的 index.html 文件名
在 Vite 项目中,默认构建 index.html 。但有时候我们需要修改 index.html 为其他文件名,比如 index-{时间戳}.html 。 我们可以这样配置 vite.config.js: import { defineConfig } from vite; import type { PluginOption } from vite;// 自…...

解决IDEA每次新建项目都需要重新配置maven的问题
每次打开IDEA都要重新配置maven,这是因为在DEA中分为项目设置和全局设置,这个时候我们就需要去到全局中设置maven了。我用的是IntelliJ IDEA 2023.3.4 (Ultimate Edition),以此为例。 第一步:打开一个空的IDEA,选择左…...

论文学习_Getafix: learning to fix bugs automatically
1. 引言 研究背景:现代生产代码库极其复杂并且不断更新。静态分析器可以帮助开发人员发现代码中的潜在问题(在本文的其余部分中称为错误),这对于在这些大型代码库中保持高代码质量是必要的。虽然通过静态分析尽早发现错误是有帮助的,但修复这些错误的问题在实践中仍然主要…...

Xilinx FPGA:vivado关于真双端口的串口传输数据的实验
一、实验内容 用一个真双端RAM,端口A和端口B同时向RAM里写入数据0-99,A端口读出单数并存入单端口RAM1中,B端口读出双数并存入但端口RAM2中,当检测到按键1到来时将RAM1中的单数读出显示到PC端,当检测到按键2到来时&…...

RedisTemplate 中序列化方式辨析
在Spring Data Redis中,RedisTemplate 是操作Redis的核心类,它提供了丰富的API来与Redis进行交互。由于Redis是一个键值存储系统,它存储的是字节序列,因此在使用RedisTemplate时,需要指定键(Key)…...
数据结构与算法基础篇--二分查找
必要前提:有序数组 算法简述:通过不断取中间值和目标target值进行比较(中间值:mid (left right) / 2) 如果目标值等于中间位置的值,则找到目标,返回中间位置如果目标值小于中间位置的值&…...
python xlsx 导出表格超链接
该Python脚本用于从Excel文件中的第一列提取所有超链接并保存到一个文本文件中。首先,脚本导入必要的库并定义输入和输出文件的路径。然后,它确保输出文件的目录存在。接着,脚本加载Excel文件并选择活动工作表。通过遍历第一列的所有单元格&a…...

Data Guard高级玩法:failover备库后,通过闪回恢复DG备库
作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等) 公众号:老苏畅谈运维 欢迎关注本人公众号,更多精彩与您分享…...

【Unity2D 2022:NPC】制作任务系统
一、接受任务 1. 编辑NPC对话脚本: (1)创建静态布尔变量用来判断ruby是否接受到任务 public class NPCDialog : MonoBehaviour {// 创建全局变量用来判断ruby是否接到任务public static bool receiveTask false; } (2ÿ…...

【C++深度学习】多态(概念虚函数抽象类)
✨ 疏影横斜水清浅,暗香浮动月黄昏 🌏 📃个人主页:island1314 🔥个人专栏:C学习 🚀 欢迎关注:👍点赞 &…...

Ubuntu 安装CGAL
一、什么是CGAL CGAL(Computational Geometry Algorithms Library)是一个广泛使用的开源库,主要用于计算几何算法的实现。该库提供了一系列高效、可靠和易于使用的几何算法和数据结构,适用于各种应用领域。以下是 CGAL 的主要功能…...
RK3568平台开发系列讲解(网络篇)netfilter框架
🚀返回专栏总目录 文章目录 一、Netfilter 介绍二、netfilter 简单案例三、防火墙功能一、Netfilter 介绍 Linux内核自2.4版本开始引入了Netfilter框架,这是一项重要的网络功能增强。Netfilter框架由Linux内核防火墙和网络维护者 Rusty Russell 所提出和实现。这个作者还基于…...
检测音视频文件的声压
FFmpeg使用 ebur128 滤镜检测声压,EBU R128 是欧洲广播联盟(European Broadcasting Union,简称 EBU)推荐的音频响度测量和归一化标准。 ffmpeg -i input_video.mp4 -filter_complex ebur128peaktrue -f null --f null -ÿ…...
计算机网络-HTTP常见面试题
目录 1. HTTP是什么?2. HTTP常见的状态码?3. HTTP 常见的字段有哪些?4. GET和POST有什么区别:5. GET 和POST方法都是安全和幂等的吗?6. HTTP缓存技术7. HTTP/1.1相比HTTP/1.0提高了什么性能?8. HTTP/2做了什…...

LNMP搭建Discuz和Wordpress
1、LNMP L:linux操作系统 N:nginx展示前端页面web服务 M:mysql数据库,保存用户和密码,以及论坛相关的内容 P:php动态请求转发的中间件 数据库的作用: 登录时验证用户名和密码 创建用户和密码 发布和…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...