首页 > 二叉树 2.0 -- 非递归遍历

二叉树 2.0 -- 非递归遍历

二叉树递归遍历存在的问题

如果我们的二叉树只有左子树,而且树的高度还很深的时候,这个时候递归调用遍历的时候,栈帧空间开辟的较大,很可能造成栈溢出。但是我们一个程序中,为堆分配的空间要比栈大的多,这个时候我们可以实现一个二叉树遍历的非递归的实现形式,模拟栈帧调用的过程,自己模拟一个栈用于保存节点,然后实现遍历。

非递归实现树的遍历

前序

首先设置一个指针cur指向根节点,访问cur然后让cur入栈,并且是在循环中使cur一直指针它的的左子树,入栈的时候就访问了,当我们出栈的时候就使cur指向它的右子树,这里我们需要两层循环,一层主要是判断当前的栈是否为空的,一层主要是判断一直往左走的时候什么时候到达了左子树的尽头了。

    void PrevNoROeder(){stack sn;Node* cur = _root;while (cur || !sn.empty()){while (cur){cout << cur->_data << " ";sn.push(cur);cur = cur->_left;}cur = (sn.top())->_right;sn.pop();}cout << endl;}

其实可以这样子理解的,我们的前序实际上是把所有的问题都转化成了访问了左子树,即使是访问右子树的时候,也好像是访问左子树一样,就是把这个树当成了一个树根

中序

同样是上面的内容,这个时候也是可以使用cur内容的,但是这个时候我们不是先访问,而是先进栈,当我们出栈的时候,我们在访问,然后出栈,出栈的时候把出来的这个节点的右子树当成是一个cur,这个时候还是一直当成是一个左子树来访问了

void InNoROrder(){stack sn;Node* cur = _root;while (cur || !sn.empty()){while (cur){sn.push(cur);cur = cur->_left;}cout << (sn.top())->_data<<" ";cur = (sn.top())->_right;sn.pop();}cout << endl;}

后序

首先让cur指向的是一个root,然后这个时候让cur一直往左走,当走到NULL的时候,我们想要出栈但是还不能出栈,因为我们还不知道当前节点的右子树是不是已经访问了呢。所以我们加上了一层判断,就是当我们的当前节点的右子树不是空的时候,并且当前节点的右子树不是刚刚出栈的节点的时候,我们就需要把当前节点的右子树。这里因为需要判断当前节点的右子树是不是刚刚出栈的节点,所以我们每次出栈的时候都需要标记一下当前出栈的节点,以便后来的比较使用。如果不是之前访问的节点,我们就可以出栈访问了。

void EndNoROrder(){stack sn;Node* cur = _root;Node* prev = _root;while (cur || !sn.empty()){if (cur)        //让所有的左子树入栈{sn.push(cur);cur = cur->_left;}else        //所有的左子树都入栈了{if (((sn.top())->_right != NULL) && ((sn.top())->_right != prev)){cur = (sn.top())->_right;}else        //所有的左右子树都访问完毕了{cout << (sn.top())->_data<<" ";prev = sn.top();sn.pop();}}}cout << endl;}

通过上面的非递归三种方式的比较我们可以发现,三个的一个共同点是,一开始都是让所有的左子树节点先入栈,其中先序遍历的时候,入栈的时候就直接访问了,中序遍历的时候是出栈的时候才访问的,还有一个点就是,出栈的时候让cur指向栈顶的右子树,先序遍历和中序遍历是直接入栈的,但是后序遍历是需要一层判断,就是如果右子树不是刚刚访问过的,说明当前右子树还没有访问过,这个时候就需要访问右子树,如果是刚刚访问过的,这个时候就可以直接让cur出栈访问就好了,就是我们在后序遍历的时候,在即将出栈的时候需要加上一层判断就是当前的右子树是否已经被访问过了,如果没有被访问过,我们就需要是当前的右子树入栈如果访问过了,我们就可以使当前的top出栈了访问他了

更多相关:

  • 背景 Linus slashdot:    https://meta.slashdot.org/story/12/10/11/0030249 Linus大婶在slashdot上回答一些编程爱好者的提问,其中一个人问他什么样的代码是他所喜好的,大婶表述了自己一些观点之后,举了一个指针的例子,解释了什么才是core low-level...

  • 给定一个整数 n, 返回从 1 到 n 的字典顺序。 例如, 给定 n =13,返回 [1,10,11,12,13,2,3,4,5,6,7,8,9] 。 请尽可能的优化算法的时间复杂度和空间复杂度。 输入的数据 n 小于等于 5,000,000。 根据题目描述,所谓字典顺序,即数值按照类似字符串首字母的ASCII大小进行排序...

  • 题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3085 题意:求n(<=10^100)之内最大的反素数。 思路: 优化2:   int prime[]= {1, 2, 3, 5, 7,11, 13, 17, 19, 23,29, 31, 37, 41,...

  • Chess Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 351    Accepted Submission(s): 124 Problem Description 小度和...

  • Node.js 中文网Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。Node.js 的包管理器 npm,是全球最大的开源库生态系统。http://nodejs.cn/先安装nodejs 在自己的网站项目文件夹根目录...

  • 对象表示方式   1、第一种方式:使用new操作符后跟Object构造函数 var person = new Object();
    person.name = 'Nicholas';
    person.age = 29; 2、对象字面量表示法 var person = {name:'Nicholas',ag...

  • 一、使用.Net Core构建WebAPI并访问Docker中的Mysql数据库 这个的过程大概与我之前的文章《尝试.Net Core—使用.Net Core + Entity FrameWork Core构建WebAPI(一)》一致。 但是在我们这里,由于docker中无法部署sql server,所以我采用了Mysql数据库,顺便...

  • 英语的重要性,毋庸置疑!尤其对广大职场人士,掌握英语意味着就多了一项竞争的技能。那,对于我们成人来说,时间是最宝贵的。如何短时间内在英语方面有所突破,这是我们最关心的事情。英语学习,到底有没有捷径可以走,是否可以速成?周老师在这里明确告诉大家,英语学习,没有绝对的捷径走,但是可以少走弯路。十多年的教学经验告诉我们,成功的学习方法可以借...

  • 展开全部 其实IDLE提供了一个显32313133353236313431303231363533e78988e69d8331333365663438示所有行和所有字符的功能。 我们打开IDLE shell或者IDLE编辑器,可以看到左下角有个Ln和Col,事实上,Ln是当前光标所在行,Col是当前光标所在列。 我们如果想得到文件代码...

  • 前言[1]从 Main 方法说起[2]走进 Tomcat 内部[3]总结[4]《Java 2019 超神之路》《Dubbo 实现原理与源码解析 —— 精品合集》《Spring 实现原理与源码解析 —— 精品合集》《MyBatis 实现原理与源码解析 —— 精品合集》《Spring MVC 实现原理与源码解析 —— 精品合集》《Spri...

  • 【本文摘要】【注】本文所述内容为学习Yjango《学习观》相关视频之后的总结,观点归Yjango所有,本文仅作为学习之用。阅读本节,会让你对英语这类运动类知识的学习豁然开朗,你会知道英语学习方面,我们的症结所在。学习英语这类运动类知识,需要把握四个原则第一,不要用主动意识。第二,关注于端对端第三,输入输出符合实际情况第四,通过多个例子...

  • 点云PCL免费知识星球,点云论文速读。文章:RGB-D SLAM with Structural Regularities作者:Yanyan Li , Raza Yunus , Nikolas Brasch , Nassir Navab and Federico Tombari编译:点云PCL代码:https://github.co...