二叉树的遍历之迭代遍历

前言:在学习二叉树的时候我们基本上已经了解过二叉树的三种遍历,对于这三种遍历,我们采用递归的思路,很简单的就能实现,那么如何用迭代的方法去解决问题?

我们首先来看第一个:

前序遍历

144. 二叉树的前序遍历 - 力扣(LeetCode)

前序遍历就是以 根 左子树 右子树的顺序遍历二叉树。呢么如何用得带去解决问题呢?

首先我们知道函数递归就是函数一层层的调用自己,本质上是以一种先进后退的思路,而这与栈的特性一致,因此函数递归,我们可以用调用堆栈来看,就是栈的思路。

现在我们解决问题,既然不用递归,但还是可以用到解决问题的本质思路,就是用栈去解决问题:

例图:

 我们就用这个图做演示:

大致解题思路:由于二叉树遍历一直往下走,无法回头,我们需要提供一个栈,假设我们每次先遍历左子树,每次遍历的时候,将我们需要往回遍历的节点放入栈中(往回遍历的节点就是拥有右子树的系点),在遍历完左节点后,此时回退上一个节点,看他的右子树是否为空。

且我们的整个循环的条件中 栈是否为空 就决定是否继续遍历这一个二叉树。

首先前序遍历的顺序 是根  左子树 右子树 。其次,对于二叉树,解决了整个左子树的遍历。那么整个右子树的遍历,也是与之相同的,也就是我们再解决问题时就只针对一个子树,这里我们就以遍历整个左子树为主,实现对整个树的遍历。且题目要求返回数组,因此我们是遍历插入数据。

由于我们无法知道某个左节点的右节点是否为空,所以开始我们先将所有左节点入栈:

第一步操作:

首先要遍历根,也就是从1开始,不为空,放入数组,并入栈,在看它的左,2不为空,放入数组,并入栈,再看它的左,3,不为空,放入数组并入栈。我们就完成了第一步,但是对于最后一个最左节点(它可能是一个子树的根,他也有可能是子树的左节点)。

第二步操作:

从这里开始,我们就要开始判断是否出栈,此时获取当前栈顶元素,3节点,再出栈。(此时栈不为空)

   若3节点无右节点,此时根已经遍历完了,现在轮到左子树了,而3此时是作为一个子树的左节点,并且是遍历顺序左子树中的第一个节点,之后出栈,看节点2的右子树是否存在。

   若3节点有右节点(右子树),比如我们这里就是节点4,3节点就往右子树走,按照前序遍历顺序,我们该将此节点数据放入数组中,因此以该节点为开始,继续第一步操作,将该右节点(也有可能是右子树),按照先把左节点入栈,入数组,在判断右子树情况来出栈。

第三步操作:

其实我们的前序遍历已经实现,可能有人还觉得还没完成,因为右子树还没处理,实际上当我们回退二叉树,也就是出栈的时候,出到最后一个元素,也就是真正的根节点时,栈不为空,循环还没结束,会继续判断当前节点的右子树。

代码实现:

vector<int> preorderTraversal(TreeNode* root) {//用来返回遍历结果的数组vector<int> v;//用栈来回退我们二叉树stack<TreeNode*> t;TreeNode*node=root;//当节点为空或者栈不为空的时候循环继续while(node||!t.empty()){//第一步将左边的节点一次放入数组并入栈 while(node){v.push_back(node->val);t.push(node);node=node->left;}//从该位置开始判断是否要出栈node=t.top();t.pop();if(node->right==nullptr){node=nullptr;//直接为空,继续循环进入到出栈操作}else{node=node->right;}      }return v;}

中序遍历

中序遍历 的顺序是 左节点 根 右子树 。与前序的插入思想不太一样,不过还是利用栈来回到上一个节点。前序遍历时,其实是有点巧合,因为根节点与左节点连续,我们是直接将左节点一个个入栈后,直接放入数组,因为顺序是从根节点开始,且根节点下来就是左节点,因此插入顺序与我们的直接将左节点插入的顺序一致。

但对于中序和后序遍历,都是先从左节点开始的,因此,从根开始,我们是先将之后的左节点一个个入栈,这里就不能再放数组了,直到最左节点时,根据我们的的遍历顺序,下来时根节点,再来判断。具体分析如下:

还是以这张图为例:

第一步:

刚开始我们还是直接以根为开始,将它的一个个左节点入栈,此时栈中为3,2,1。

第二步:

从这里开始,就需要判断是否出栈,是否插入数组,因此从这里开始,还是先获取栈顶元素,在出栈,当前位置就是节点3,之后就是判断3节点是否具有右子树。

若没有右子树,那么3就是第一个左节点,放入数组,之后回退到上一个节点2,继续判断。

若有右子树,此时节点3是一个根,不能放入数组,我们继续走到节点4,以4节点为开始,与前序遍历一样,执行第一步的操作进行入栈,之后在判断。

直到回退到根节点,此时进行右子树的遍历。

代码如下:

  vector<int> inorderTraversal(TreeNode* root) {vector<int> v;stack<TreeNode*> t;TreeNode* prev=nullptr;while(root||!t.empty()){while(root){if(root){t.push(root);}root=root->left;}root=t.top();t.pop();      //为空先插左节点v.push_back(root->val);if(root->right==nullptr){root=nullptr;}else{root=root->right;}}return v;}

 后序遍历

后序遍历的思路与中序遍历有些许一样,但对于情况的判断更加复杂。

因为后序的遍历顺序为 左子树  右子树 根,育贤婿一样,我们还是从根节点开始,入栈左子树的所有左节点,直到最左节点,还是要进行判断。具体分析如下:

 还是一这张图为例:

第一步:

依然是将左子树的所有的左节点依次入栈, 1入,2入,3入,此时到了节点3。

第二步:

从节点3开始判断是否要回退,因此获取栈顶节点为3,出栈,当前节点为3。

若3没有右节点,此时我们就是左节点,我们就可以将3放入数组,之后回退到节点2。

若3的右节点存在,此时的操作是将该节点再次重新入栈(因为下一个右节点(右子树)插入完才轮到我),之后当前节点走到这个右节点,循环回到第一步,继续入栈。

在这里有重要的一点:

第一点:与之前两种遍历一样,我们需要从当前节点回退到上个节点的方法是将指针置空,之后重新赋值为栈顶元素的节点。

其次除了叶子节点容易判断插入,如果上一次插入的节点,是当前节点的右节点,则就需要放入数组里,即 先插的左, 之后插的右,根节点需要判断,当前节点的右节点是上一次插入的节点就说明是根,插入数组。

源码:

vector<int> postorderTraversal(TreeNode* root) {stack<TreeNode*>s;vector<int> ret;TreeNode*prev=nullptr;while(root||!s.empty()){//先找所有的左节点while(root){ s.push(root);root=root->left;}//倒退判断这些节点的右节点,直到最后根节点,在判断右子树root=s.top(); s.pop();if(root->right==nullptr ||root->right==prev) {ret.push_back(root->val);prev=root;root=nullptr;}else{//若右子树不为空,继续入栈s.push(root);root=root->right;}}return ret;}

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

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

相关文章

【计算机网络学习之路】HTTP请求

目录 前言 HTTP请求报文格式 一. 请求行 HTTP请求方法 GET和POST的区别 URL 二. 请求头 常见的Header 常见的额请求体数据类型 三. 请求体 结束语 前言 HTTP是应用层的一个协议。实际我们访问一个网页&#xff0c;都会像该网页的服务器发送HTTP请求&#xff0c;服务…

chrome 调试之 - 给微软小冰看病(无论给小冰发送什么内容都只回复“我已经开始升级啦,期待一下吧!”)

微软 Bing 搜索推出了小冰AI智能聊天模块&#xff0c;具体启用方式是用edge或chrome浏览器打开链接 cn.bing.com 后在输入框搜索任意内容&#xff0c;待搜索结果页面加载完并稍等片刻&#xff0c;页面右侧就会出现一个躲在滚动条后面的小萝莉&#xff0c;抚摸...不&#xff0c;…

Java-网络通信总结

文章目录 网络程序设计基础局域网与互联网 网络协议IP协议TCP/IP 协议端口域套接字 TCP 程序InterAddress 类ServerSocket 类 UDP 程序DatagramPacket 类DatagramSocket 类 网络程序设计基础 网络程序设计编写的是与其他计算机进行通信的程序。Java 已经将网络程序所需要的元素…

RK3588平台开发系列讲解(hardware)reference-ril源码分析

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、reference-ril目录介绍二、支持的功能三、Android RIL 框架沉淀、分享、成长,让自己和他人都能有所收获!😄 一、reference-ril目录介绍 目录:3588-android12/hardware/ril/reference-ril

ElementUI 时间选择器如何限定选择时间

DatePicker 日期选择器 | Element Plus 我们如何限定我们的选择时间呢&#xff0c;比如限定选择时间为今天之前&#xff0c;或者今天之后的时间&#xff1f; 我们可以使用官方提供的disabled-date来实现 我们通过这个属性 做一个回调函数&#xff0c;在里面比较我们想要限定的时…

鸿蒙方舟开发框架ArkUI简介

语雀知识库地址&#xff1a;语雀HarmonyOS知识库 飞书知识库地址&#xff1a;飞书HarmonyOS知识库 嗨&#xff0c;各位别来无恙呐&#xff0c;我是小白 众所周知&#xff0c;华为在今年推出了 HarmonyOS 4.0 版本&#xff0c;而在此之前的版本中&#xff0c;HarmonyOS 应用的 …

2024年AI视频识别技术的6大发展趋势预测

随着人工智能技术的快速发展&#xff0c;AI视频识别技术也将会得到进一步的发展和应用。2023年已经进入尾声&#xff0c;2024年即将来临&#xff0c;那么AI视频识别技术又将迎来怎样的发展趋势&#xff1f;本文将对2023年的AI视频技术做一个简单的盘点并对2024年的发展趋势进行…

Advanced Renamer

Advanced Renamer 安装链接 1.前后添加字符 2.字符转数字&#xff0c;编号整体加减

oracle实验2023-12-8--触发器

第十四周实验 【例】功能要求&#xff1a;增加一新表XS_1&#xff0c;表结构和表XS相同&#xff0c;用来存放从XS表中删除的记录。 分析: 1、创建表 xs_1 SQL> create table xs_1 as select * from xs; Table created SQL> truncate table xs_1; Table truncated题目&a…

StoneDB-8.0-V2.2.0 企业版正式发布!性能优化,稳定性提升,持续公测中!

​ 11月&#xff0c;StoneDB 新版本如期而至&#xff0c;这一个月来我们的研发同学加班加点&#xff0c;持续迭代&#xff1a;在 2.2.0 版本中&#xff0c;我们针对用户提出的需求和做出了重量级更新&#xff0c;修复了一些已知和用户反馈的 Bug&#xff0c;同时对部分代码进行…

PairLIE论文阅读笔记

PairLIE论文阅读笔记 论文为2023CVPR的Learning a Simple Low-light Image Enhancer from Paired Low-light Instances.论文链接如下&#xff1a; openaccess.thecvf.com/content/CVPR2023/papers/Fu_Learning_a_Simple_Low-Light_Image_Enhancer_From_Paired_Low-Light_Instan…

Kafka安全性探究:构建可信赖的分布式消息系统

在本文中&#xff0c;将研究Kafka的安全性&#xff0c;探讨如何确保数据在传输和存储过程中的完整性、机密性以及授权访问。通过详实的示例代码&#xff0c;全面讨论Kafka安全性的各个方面&#xff0c;从加密通信到访问控制&#xff0c;帮助大家构建一个可信赖的分布式消息系统…

Vue:用IDEA开发Vue,标签语法爆红问题处理

一、场景描述 我在IDEA中&#xff0c;学习Vue课程。 入门学习时&#xff0c;是在html文件中&#xff0c;script引入vue.js文件方式。 此时&#xff0c;在html文件中用v-标签&#xff0c;爆红。 二、解决办法 打开 菜单栏 File - Settings 选择 Editor - Files Type&#xf…

《每天一个Linux命令》 -- (5)通过sshkey密钥登录服务器

欢迎阅读《每天一个Linux命令》系列&#xff01;在本篇文章中&#xff0c;将介绍通过密钥生成&#xff0c;使用公钥连接管理服务器。 概念 SSH 密钥是用于安全地访问远程服务器的一种方法。SSH 密钥由一对密钥组成&#xff1a;公钥和私钥。公钥存储在远程服务器上&#xff0c;…

软件工程复习

一、题型 单项选择题 20分 填空题 10分 判断题 10分 简答题 18分 应用题 12分 综合题 30分 软件程序数据文档 软件是无形的、不可见的逻辑实体 20世纪60年代末爆发软件危机 软件危机是指软件在开发与维护过程中遇到的一系列严重的问题 …

理解 GET、POST、PATCH 和 DELETE 请求的参数传递方式

理解 GET、POST、PATCH 和 DELETE 请求的参数传递方式 本文将向您介绍在使用 GET、POST、PATCH 和 DELETE 请求时如何传递参数。通过详细解释每种请求的参数传递方式和示例代码&#xff0c;您将了解如何正确地将数据发送到服务器并与之交互。 GET 请求的参数传递方式 在 GET…

CentOS 7.9安装宝塔面板,安装gitlab服务器

docker安装方式比较慢&#xff0c;安装包1.3GB 安装后启动很慢 docker logs q18qgztxdvozdv_gitlab-ce-gitlab-1 docker ps docker exec -it q18qgztxdvozdv_gitlab-ce-gitlab-1 sh cd /etc/gitlab cat initial_root_password 软件商店安装方式&#xff0c;失败了2023.12…

hook其他调试技巧

输出堆栈信息 通过 android.util.Log 输出当前线程的堆栈跟踪信息。 function showStacks() {Java.perform(function () {console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new() )); }) } 可以在需要的…

机器学习--稀疏学习

前置知识&#xff1a; 通常学习一次模型的过程如下&#xff1a;我们普遍为了获取更好的模型效果&#xff0c;直接对原始数据学习&#xff0c;会造成过拟合、需要特征提取&#xff1b; 而若特征提取完后依旧有很多特征&#xff0c;还是会容易过拟合。这时候就需要特征降维和特…

C/C++端口复用SO_REUSEADDR(setsockopt参数),test ok

端口复用最常用的用途应该是防止服务器重启时之前绑定的端口还未释放或者程序突然退出而系统没有释放端口。这种情况下如果设定了端口复用&#xff0c;则新启动的服务器进程可以直接绑定端口。如果没有设定端口复用&#xff0c;绑定会失败&#xff0c;提示ADDR已经在使用中——…