使用递归形式以及迭代形式实现树的前中后序遍历

  相信大家对于二叉树的遍历并不陌生,对于二叉树的递归遍历我们也可以信手拈来。但是如果让我们将二叉树修改成为非递归的形式呢?是不是有点疑惑了?那么本次博客我们就来梳理一下二叉树的非递归遍历。

  由于递归遍历二叉树的代码以及逻辑都很简单,所以我们直接给出代码的结果,之后直接进行输出结果的比较即可。

  我们手动构建的树的形式如下图所示。

  

  遍历结果如下:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;//创建一个节点,用于创建树的节点
typedef struct BTNodeTree
{int _val;struct BTNodeTree* left;struct BTNodeTree* right;BTNodeTree(int data):_val(data),left(nullptr),right(nullptr){}
}*BTNode;//使用递归的形式实现二叉树的前中后序遍历
void preOrder(BTNode root)
{if (root == nullptr){return;}cout << root->_val << " ";preOrder(root->left);preOrder(root->right);
}void midOrder(BTNode root)
{if (root == nullptr){return;}midOrder(root->left);cout << root->_val << " ";midOrder(root->right);
}void posOrder(BTNode root)
{if (root == nullptr){return;}posOrder(root->left);posOrder(root->right);cout << root->_val << " ";
}
BTNode& createTree()
{//由于我们从堆当中申请空间,在此函数结束的时候,并不会释放栈帧,因此可以使用&返回BTNode data1 = new BTNodeTree(1);BTNode data2 = new BTNodeTree(2);BTNode data3 = new BTNodeTree(3);BTNode data4 = new BTNodeTree(4);BTNode data5 = new BTNodeTree(5);BTNode data6 = new BTNodeTree(6);BTNode data7 = new BTNodeTree(7);BTNode data8 = new BTNodeTree(8);BTNode data9 = new BTNodeTree(9);BTNode data10 = new BTNodeTree(10);data1->left = data2;data1->right = data3;data2->left = data4;data2->right = data5;data3->left = data6;data3->right = data7;data4->left = data8;data4->right = data9;data5->left = data10;return data1;
}
int main()
{BTNode root = createTree();cout << "preOrder:";preOrder(root);cout << endl;cout << "midOrder:";midOrder(root);cout << endl;cout << "posOrder";posOrder(root);cout << endl;return 0;
}

     使用栈编写迭代形式的树的遍历

  之后的内容就是我们本次博客的重头戏了。我们需要将上述的过程修改成为非递归的形式。(为了便于区分我们可以将递归的形式遍历树的操作封装到一个命名空间域当中,避免访问冲突的情况产生)根据我们所学的知识来说:所有的递归函数都会创建一个栈帧,之后以栈的形式进行访问并输出数据,以达到我们的预期效果,所以我们就可以使用栈这个数据结构模拟实现我们栈帧的开辟。

   先序遍历

  首先是我们的先序遍历。首先我们需要创建一个栈,之后根据我们的输出要求向栈当中压入数据。首先我们向栈中压入我们的首节点,之后根据栈是否为空进行判断是否需要继续循环。之后我们因为需要先读取左子树当中的数据,因此我们栈顶的元素应该是左子树的数据节点,那么也就是说我们需要先向栈当中加入右子树的节点。之后每一次进行循环的时候,从栈当中拿出一个数据,删除并加入其左右子树即可。过程如图所示:

  根据上述步骤我们可以编写出如下的代码:

void preOrder(BTNode root)
{stack<BTNode> ret;ret.push(root);while (!ret.empty()){cout << ret.top()->_val << " ";BTNode tmp = ret.top();ret.pop();if (tmp->right){ret.push(tmp->right);}if (tmp->left){ret.push(tmp->left);}}
}
   中序遍历

  想要进行中序遍历操作,我们需要先清楚中序遍历执行遍历节点的步骤。首先我们需要先访问左子树,当左子树已经访问完毕之后才访问根节点进行打印数据。因此我们就需要设置一个指针进行遍历操作。首先我们从根节点开始,向左进行执行,如果左子树不为空就将该节点压入栈中继续访问,形成循环,当我们的左子树为空的时候,我们就需要将栈顶的元素赋值给cur,打印输出并访问右子树。过程如图所示:

  根据上述步骤我们可以编写出如下代码:

void midOrder(BTNode root)
{stack<BTNode> ret;BTNode cur = root;while (cur != nullptr || !ret.empty()){if (cur){ret.push(cur);cur = cur->left;}else{cur = ret.top();ret.pop();cout << cur->_val << " ";cur = cur->right;}}
}
  后序遍历

  对于后序遍历的实现我们有两种方式。

    后序遍历1:

  我们可以根据后序遍历的特点进行观察,后序遍历操作会先访问左子树,之后再访问右子树,等到所有的节点都访问完毕的时候我们才会输出相应的数据。所以我们的执行顺序就为:左右根

  而我们的前序遍历,我们需要进行的遍历顺序为:根左右。那是不是说我们只需要对前序遍历进行略微的修改,再进行逆序就可以得到我们后序遍历的执行效果呢?我们可以尝试编写代码:

  需要注意的是:对于前序遍历我们需要修改加入栈中的节点的顺序,如果想要逆序得到左右根的话,我们正序就需要是根右左,我们就需要将栈顶元素时刻保持为右子树的状态,这是和前序遍历的唯一区别。

void posOrder1(BTNode root)
{vector<int> data;stack<BTNode> ret;ret.push(root);while (!ret.empty()){data.push_back(ret.top()->_val);BTNode tmp = ret.top();ret.pop();if (tmp->left){ret.push(tmp->left);}if (tmp->right){ret.push(tmp->right);}}reverse(data.begin(), data.end());for (auto e : data){cout << e << " ";}
}

    后序遍历2:

  前面的后序遍历是使用先序遍历进行复现的,那么有没有方法可以直接实现后序遍历呢?当然有了。这个方法和我们的中序遍历有些许类似之处,但是我们需要多设置几个变量进行数据的保存。

  首先我们需要设置一个栈用于保存节点的数据。之后我们还需要设置一个cur指针,用于作为节点移动的标志,我们还需要设置一个prev指针,用于记录我们是否已经遍历过右边子树的节点,防止重复遍历。因为我们的遍历顺序是:左右根,根是最后访问的,那么我们就需要遍历完右子树之后再访问根节点。但是我们有需要和从根节点进入右节点当中的情况进行区分,所以我们需要设置一个prev指针进行记录。其中保存的是刚刚执行过操作的右子树的节点的指针。

  我们需要进行的操作大致如下:首先我们需要找到左子树的根节点,当我们一直进行访问并将节点的指针加入到栈当中,直到遇到空节点之后。这个时候cur指向nullptr,我们需要让cur拿到栈顶的元素,也就是最近的左子树的节点,这个时候我们需要对该节点的右子树进行判断。因为我们想要先访问右子树的节点。所以如果右子树的节点不为空的时候我们需要将cur的值修改成为右子树的指针。如果为空就删除拿到的top节点。并打印输出数据。因为这个时候我们的右子树为空,也就是说应该遍历根节点了。在打印输出数据之后,就代表以该节点为根节点的树已经全部遍历完成了。那么我们就需要将prev设置成为该节点的指针,防止在执行下一次操作的时候,重复进入该子树当中造成死循环。过程如图所示:

  根据上述步骤我们可以编写出如下的代码:

void posOrder2(BTNode root)
{stack<BTNode> ret;BTNode prev = nullptr;BTNode cur = root;while (cur|| !ret.empty()){while (cur){ret.push(cur);cur = cur->left;}BTNode top = ret.top();if (top->right == nullptr || top->right == prev){ret.pop();cout << top->_val << " ";prev = top;}else{cur = top->right;}}
}

  测试一遍我们使用栈进行树的遍历的输出结果:

  对比我们之前使用递归的形式编写的代码,结果一切正常。

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

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

相关文章

(函数)判断素数(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>//声明素数判断函数&#xff1b; void prime(int number);int main() {//初始化变量值&#xff1b;int number 0;//获取用户输入的数据&#xff1b;printf(&quo…

AI在肿瘤学临床决策中的应用:一种多模态方法

在临床肿瘤学领域&#xff0c;多模态人工智能&#xff08;AI&#xff09;系统通过解读各类医学数据&#xff0c;展现出提升临床决策的潜力。然而&#xff0c;这些模型在所有医学领域中的有效性尚未确定。本文介绍了一种新型的多模态医疗AI方法&#xff0c;该方法利用大型语言模…

JAVA 17

文章目录 概述一 语法层面变化1_JEP 409&#xff1a;密封类2_JEP 406&#xff1a;switch模式匹配&#xff08;预览&#xff09; 二 API层面变化1_JEP 414&#xff1a;Vector API&#xff08;第二个孵化器&#xff09;2_JEP 415&#xff1a;特定于上下文的反序列化过滤器 三 其他…

手机投屏技巧:手机怎么投屏到电脑显示屏上?精选6招解决!

手机怎么投屏到电脑显示屏上&#xff1f;出于一些不同的原因&#xff0c;大多数人都希望能将手机投屏到电脑上。其中一个常见的原因是&#xff0c;大家经常会希望在笔记本电脑上共享图片&#xff0c;而无需上传或者登录微信进行文件传输。以及希望不依靠投影仪&#xff0c;就能…

只刷题可以通过PMP考试吗?

咱们都知道&#xff0c;PMBOK那本书&#xff0c;哎呀&#xff0c;读起来确实有点费劲。所以&#xff0c;有些人就想了&#xff0c;干脆我就刷题吧&#xff0c;题海战术&#xff0c;没准儿也能过。这话啊&#xff0c;听起来似乎有点道理&#xff0c;但咱们得好好琢磨琢磨。 刷题…

【YashanDB知识库】自动选举配置错误引发的一系列问题

问题现象 问题出现的步骤/操作&#xff1a; ● 配置自动选举&#xff0c;数据库备库手动发起switch over&#xff0c;命令会报错 ● 主、备库变为只读状态&#xff0c;数据库无法进行读写操作 ● shutdown immediate 停止数据库&#xff0c;此时发现数据库一直没有退出&…

论文笔记:Vision GNN: An Image is Worth Graph of Nodes

neurips 2022 首次将图神经网络用于视觉任务&#xff0c;同时能取得很好的效果 1 方法 2 架构 在计算机视觉领域&#xff0c;常用的 transformer 通常是 isotropic 的架构&#xff08;如 ViT&#xff09;&#xff0c;而 CNN 更喜欢使用 pyramid 架构&#xff08;如 ResNet&am…

开源数据库同步工具DBSyncer

前言&#xff1a; 这么实用的工具&#xff0c;竟然今天才发现&#xff0c;相见恨晚呀&#xff01;&#xff01;&#xff01;&#xff01; DBSyncer&#xff08;英[dbsɪŋkɜː]&#xff0c;美[dbsɪŋkɜː 简称dbs&#xff09;是一款开源的数据同步中间件&#xff0c;提供M…

必看项目|多维度揭示心力衰竭患者生存关键因素(生存分析、统计检验、随机森林)

1.项目背景 心力衰竭是一种严重的公共卫生问题,影响着全球数百万人的生活质量和寿命,心力衰竭的病因复杂多样,既有个体生理因素的影响,也受到环境和社会因素的制约,个体的生活方式、饮食结构和医疗状况在很大程度上决定了其心力衰竭的风险。在现代社会,随着生活水平的提…

使用moquette mqtt发布wss服务

文章目录 概要一、制作的ssl证书二、配置wss小结 概要 moquette是一款不错的开源mqtt中间件&#xff0c;github地址&#xff1a;https://github.com/moquette-io/moquette。我们在发布mqtt服务的同时&#xff0c;是可以提供websocket服务器的&#xff0c;有些场景下需要用到&a…

OpenAI新模型开始训练!GPT6?

国内可用潘多拉镜像站GPT-4o、GPT-4&#xff08;更多信息请加Q群865143845&#xff09;: 站点&#xff1a;https://xgpt4.ai0.cn/ OpenAI 官网 28 日发文称&#xff0c;新模型已经开始训练&#xff01; 一、新模型开始训练 原话&#xff1a;OpenAI has recently begun training…

价值飙升30%,AI PC拉动半导体出货潮

由于处理器和DRAM的升级&#xff0c;大摩预测每台AI PC的半导体价值将增长20%-30%&#xff0c;PC平均售价也将提高7%。 台北国际电脑展即将于6月2日隆重开幕。 随着展会的临近&#xff0c;各种现象级的AI PC也蓄势待发。 就在上周&#xff0c;联想在业绩会上&#xff0c;首次…

2-EMMC启动及各分区文件生成过程

EMMC的使用比nand flash还是复杂一些&#xff0c;有其特有的分区和电器性能 1、启动过程介绍 跟普通nand或spi flash不同&#xff0c;uboot前面还有好几级 在vendor某些厂商的设计中&#xff0c;ATF并不是BOOTROM加载后的第一个启动镜像&#xff0c;可能是这样的&#xff1a; …

java的方法重写

重写的概述 重写是基于继承来说的&#xff0c;因为父类的方法需求不满足于子类&#xff0c;所以就要在进行方法重写&#xff0c;如果不知道继承是啥可以看我上一篇笔记 在这里用代码举个栗子 例如&#xff1a;我们定义了一个动物类代码如下&#xff1a; public class Animal…

Leecode热题100---二分查找--4:寻找两个正序数组的中位数

题目&#xff1a; 给定两个大小分别为 m 和 n 的正序&#xff08;从小到大&#xff09;数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 解法1、暴力解法&#xff08;归并&#xff09; 思路&#xff1a; 合并 nums1&#xff0c;nums2 为第三个数组 排序第三个数…

XXL-JOB分布式任务调度框架详解(全网最详细!!!)

​​​​​​​ 引言 第一部分&#xff1a;XXL-JOB概述 第二部分&#xff1a;架构与组件 第三部分&#xff1a;使用教程 第四部分&#xff1a;源码分析 第五部分&#xff1a;最佳实践 引言 在分布式系统中&#xff0c;任务调度是一项基础而又关键的服务&#xff0c;它涉…

Java设计模式:享元模式实现高效对象共享与内存优化(十一)

码到三十五 &#xff1a; 个人主页 目录 一、引言二、享元设计模式的概念1. 对象状态的划分2. 共享机制 三、享元设计模式的组成四、享元设计模式的工作原理五、享元模式的使用六、享元设计模式的优点和适用场景结语 [参见]&#xff1a; Java设计模式&#xff1a;核心概述&…

拼接字符串

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 使用“”运算符可完成对多个字符串的拼接&#xff0c;“”运算符可以连接多个字符串并产生一个字符串对象。 例如&#xff0c;定义两个字符串&#…

任务3.1:采用面向对象方式求三角形面积

面向对象编程&#xff08;OOP&#xff09;是一种将现实世界中的实体抽象为对象&#xff0c;并通过类和对象来模拟现实世界中的行为和属性的编程范式。在本实战任务中&#xff0c;我们通过创建一个Triangle类来模拟现实世界中的三角形&#xff0c;并使用面向对象的方法来求解三角…

「清新题精讲」CF249D - Donkey and Stars

更好的阅读体验 CF249D - Donkey and Stars Description 给定 n n n 个点 ( x i , y i ) (x_i,y_i) (xi​,yi​) 和 a , b , c , d a,b,c,d a,b,c,d&#xff0c;求出最多有多少个点依次连接而成的折线上线段的斜率在 ( a b , c d ) (\frac{a}{b},\frac{c}{d}) (ba​,dc​…