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

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

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

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

  

  遍历结果如下:

#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;
		}
	}
}

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android Context 详解

一、什么是Context&#xff1f; Context是一个抽象基类。在翻译为上下文&#xff0c;是提供一些程序的运行环境基础信息。 Context下有两个子类&#xff0c;ContextWrapper是上下文功能的封装类&#xff08;起到方法传递的作用&#xff0c;主要实现还是ContextImpl&#xff0…

操作系统实验三 可变分区内存分配首次适应算法模拟

实验三 可变分区内存分配首次适应算法模拟 实验内容 模拟内存分配&#xff0c;了解并掌握动态分区分配中所用的数据结构、分区分配算法&#xff0c;深刻理解首次适应内存分配算法。 模拟实现可变分区内存分配首次适应算法&#xff1b;空闲分区表要求有空闲块的起始地址、大小…

6.2 Go 切片(Slice)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

惠海半导体 30V-60V-100V-150VMOS管 打火机、加湿器NMOS管 高耐压

MOS管的工作原理 MOS管&#xff0c;即金属-氧化物-半导体场效应晶体管&#xff0c;是一种重要的电子元件&#xff0c;在电路中起着关键的作用。其工作原理主要基于半导体材料的特性以及电场对电荷的控制。 首先&#xff0c;MOS管的基本结构包括源极、栅极和漏极。其中&#xf…

JUC框架(Future CompletableFuture详解)

文章目录 FutureFuture介绍Future原理Future代码示例 CompletableFutureCompletableFuture特点应用场景方法特性方法刨析supplyAsync/runAsyncthenApply / thenApplyAsync异步回调thenAccept / thenRunexceptionallywhenCompletehandle 实现场景 更多相关内容可查看 Future Fu…

实操专区-第15周-课堂练习专区-漏斗图与金字塔图

实操专区-第15周-课堂练习专区-漏斗图 下载安装ECharts&#xff0c;完成如下样式图形。 代码和截图上传 基本要求&#xff1a;下图3选1&#xff0c;完成代码和截图 完成 3.1.3.16 漏斗图中的任务点 基本要求&#xff1a;2个选一个完成&#xff0c;多做1个加2分。 请用班级学号姓…

有趣的css - 列表块加载动效

大家好&#xff0c;我是 Just&#xff0c;这里是「设计师工作日常」&#xff0c;今天分享的是用 css 打造一个极简的列表块加载动效。 最新文章通过公众号「设计师工作日常」发布。 目录 整体效果核心代码html 代码css 部分代码 完整代码如下html 页面css 样式页面渲染效果 整…

Linux shell命令

cat 文件名 查看文件内容&#xff0c; tac文件名 倒着显示。 more 文件名 显示内容 less文件名 和more的功能一样&#xff0c;按上下左右键&#xff0c;按Q键结束。 head文件名&#xff0c;只显示前10行内容。 ln是一个默认创建硬链接的命令 ln 文件名 ls -i文件名…

【每日力扣】300. 最长递增子序列 与 139. 单词拆分

&#x1f525; 个人主页: 黑洞晓威 &#x1f600;你不必等到非常厉害&#xff0c;才敢开始&#xff0c;你需要开始&#xff0c;才会变的非常厉害 300. 最长递增子序列 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&…

JEPaaS 低代码平台 accessToTeanantInfo SQL注入漏洞复现

0x01 产品简介 JEPaaS低代码开发平台开源版 旨在帮助企业快速实现信息化和数字化转型。该平台基于可视化开发环境,让软件开发人员和业务用户通过直观的可视化界面来构建应用程序 ,而不是传统的编写代码方式。 用户可以在开发平台灵活各个图形化控件,以构建业务流程、逻辑和…

Linux基础学习笔记

目录 1、Linux安装 1.1 安装教程 1.2 Linux目录结构 2、Linux常用命令 2.1 ls 2.2 命令分类 2.3 目录处理命令 2.4 操作文件命令 2.5 查找文件命令 2.6 ln链接命令 2.7 进程相关命令 ​编辑3、配置网络 3.1 关闭windows防火墙 3.2 配置好虚拟机的局域网 3.3 配置…

电焰灶:引领未来厨房烹饪革命,创“灶”家庭幸福感和安全感

随着科技的飞速发展&#xff0c;厨房灶具也迎来了前所未有的革新。电焰灶&#xff0c;这一新型的灶具&#xff0c;正以其独特的优势&#xff0c;逐渐取代传统的燃气灶和电磁炉&#xff0c;开启了一场灶具的新时代。它以其方便、节能的特点&#xff0c;让烹饪变得更加轻松高效&a…

ITIL4认证考试这么贵,还值得考证吗,有必要学吗?

从2023年4月1日开始&#xff0c;ITIL 4是Foundation认证将会捆绑OTM(Official Training Materials),这样在一次ITIL4的考试费中将会捆绑&#xff1a;试卷费电子教材书费监考费OTM费&#xff0c;每一种考试费都相较于2022年有涨幅&#xff0c;再加上PeopleCert收取的授权机构的授…

【喜报】科大睿智多家服务企业上榜2024年第四批DCMM名单

近日&#xff0c;DCMM官方平台发布通知公告&#xff0c;根据《数据管理能力成熟度评估工作管理办法(暂行)》的有关规定&#xff0c;经单位自愿申请&#xff0c;评估机构评估、专家评审及公示&#xff0c;下列27单位获得数据管理能力成熟度等级证书。小编祝贺多家服务企业上榜20…

神经网络不确定性综述(Part V)——Uncertainty measures and quality

相关链接&#xff1a; 神经网络不确定性综述(Part I)——A survey of uncertainty in deep neural networks-CSDN博客 神经网络不确定性综述(Part II)——Uncertainty estimation_Single deterministic methods-CSDN博客 神经网络不确定性综述(Part III)——Uncertainty est…

探索 ChatboxAI:智能对话的新时代

在人工智能迅速发展的今天&#xff0c;智能对话已经成为了我们日常生活中不可或缺的一部分。从智能助理到聊天机器人&#xff0c;AI 技术正在改变我们与世界互动的方式。今天&#xff0c;我们要介绍的是一个全新且功能强大的平台——ChatboxAI。 什么是 ChatboxAI&#xff1f;…

PyTorch自定义张量操作开发指南【CFFI+CUDA】

PyTorch 与 TensorFlow 一起成为深度学习研究人员和从业者的标准。虽然 PyTorch 在张量运算或深度学习层方面提供了多种选择&#xff0c;但一些专门的操作仍然需要手动实现。在运行时至关重要的情况下&#xff0c;应使用 C 或 CUDA 来完成此操作&#xff0c;以支持 CPU 和 GPU …

智能除螨—wtn6040-8s语音芯片方案引领除螨仪新时代

语音螨仪开发背景&#xff1a; 随着物联网技术的快速发展&#xff0c;除螨仪作为家庭清洁的重要工具&#xff0c;其智能化、人性化的设计成为提升市场竞争力的关键。置入语音芯片的除螨仪&#xff0c;通过开机提示、工作状态反馈、操作指引、故障提醒等内容。用户可以更加直观…

.NET 某和OA办公系统全局绕过漏洞分析

转自先知社区 作者&#xff1a;dot.Net安全矩阵 原文链接&#xff1a;.NET 某和OA办公系统全局绕过漏洞分析 - 先知社区 0x01 前言 某和OA协同办公管理系统C6软件共有20多个应用模块&#xff0c;160多个应用子模块&#xff0c;从功能型的协同办公平台上升到管理型协同管理平…

腾讯社招测试岗有点奇葩的面试,被问抽奖程序的测试用例设计

今天腾讯网上预约社会招聘&#xff0c;我是前天才看到这条消息&#xff0c;前天投了简历&#xff0c;还叫别人内推了我一把&#xff0c;但是悲剧的我把简历上的号码写成了原来在北京的号码&#xff0c;所以我也不知道是别人觉得我简历不合适还是因为联系不上我所以没有邀请我参…