非递归前序、中序遍历代码推演出后序遍历代码(极其透彻)

65 篇文章 5 订阅
订阅专栏

一、前言

众所周知,二叉树的遍历方式有三种:前序遍历、中序遍历和后序遍历。

🍌 前序遍历:首先访问根节点,然后递归遍历左子树,最后递归遍历右子树。

🍌 中序遍历:首先递归遍历左子树,然后访问根节点,最后递归遍历右子树。

🍌 后序遍历:首先递归遍历左子树,然后递归遍历右子树,最后访问根节点。

上面三种遍历方式每一种又存在两种实现形式:递归和非递归。

 🍓 递归遍历二叉树:在函数内通过调用自身来完成遍历的一种方式,本质是利用函数调用栈来实现的,每次递归都要建立一个新的函数栈帧,效率较低,空间复杂度较大,更适合深度优先搜索。

🍓 非递归遍历二叉树:利用循环来遍历二叉树,用自己定义的栈来实现的,更适合广度优先搜索。

深度优先搜索(DFS):从根节点出发,先搜索左子树,再搜索右子树的遍历方式。这种遍历方式可以使用递归来实现。

广度优先搜索(BFS):从根节点开始,按层次遍历二叉树的方式。这种遍历方式通常不使用递归来实现,而是使用队列来实现。


二、非递归前序和中序遍历算法

下面是非递归前序和中序遍历对比(代码在下方可拷贝,代码含义详见另一篇文章戳),我们可以看出两种非递归遍历方式只是访问跟根节点的位置不同,这显然是废话!仔细看下,有没有什么新发现?如果实在是没有那就继续往下看吧。

非递归前序和中序遍历对比
// 先序非递归遍历
void pre_order_traverse_none(BiTree bitree)
{
    // 思路:先序顺序为:根节点->左子树->右子树
    // 当遍历完左子树之后找不到右子树
    // 解决方法:记住根节点
    
    stack<BiTree> stk;
    BiTree p = bitree;
    // 当前节点不为空,或者栈不为空
    while(p || !stk.empty())
    {
        if(p)// 根节点不为空
        {
            cout << p->data<< ",";
            stk.push(p);// 根指针入栈
            p = p->lchild;// 遍历左子树
        }
        else// 栈不为空
        {
            p = stk.top();// 获取根节点
            stk.pop();// 弹出根节点
            p = p->rchild;// 访问右子树
        }
    }

}

// 中序非递归遍历
void in_order_traverse_none(BiTree &bitree)
{
    // 思路:中序顺序为:左子树->根节点->右子树
    // 当遍历完左子树之后找不到根节点
    // 解决方法:记住根节点

    BiTree p = bitree;
    stack<BiTree> stk;

    while(p || !stk.empty())
    {
        if(p)// 如果根节点非空,压栈,记录根节点
        {
            stk.push(p);
            p = p->lchild;// 首先遍历左子树
        }
        else// 栈非空,弹出根节点
        {
            p = stk.top();
            cout << p->data<<" " ;
            stk.pop();
            p = p->rchild;// 访问右子树
        }
    }
}

从上面的代码对比中我们可以看出,在入栈时进行了根节点访问,访问方式即为非递归前序遍历,在弹栈时进行根节点访问,访问方式是非递归中序遍历,也就是说(敲黑板,重点来了):

🍊 入栈时  ->  三个结点一个都未访问  ->  提供前序遍历的机会

🍊 出栈时  -> (不同根节点的)左子树遍历完成  ->  提供中序遍历的机会

第一点大家应该都能看懂,但不出意外的话,第二点标红的地方应该出现了意外,利用下面的二叉树进行解释。

示例二叉树

在指向结点4的时候,首先将结点4压栈,并访问结点4的左子树,左子树为空,按照上面的代码,此时需要弹栈,栈顶为结点4,那么此时弹栈是因为结点4的左子树遍历完成。

另一种情况,遍历到5的时候,同样先将5压栈,再访问左子树,左子树为空,同样将5弹栈,这类似于上一种情况,即5的左子树遍历完成需要弹栈。

然后再遍历5的右子树,右子树为空,根据上方代码,同样需要弹栈,但是,注意,此时直接将1弹栈(2已经遍历过哦),仔细一看这里弹栈是因为1的左子树遍历完成了!其他情况自行尝试,现在应该明白上面的红体字了吧。

言归正传,上面的非递归前序中序遍历代码只能提供入栈和出栈时的两种遍历方式,对于非递归后序遍历无效,为什么这样说呢?又怎么修改代码呢?好的,我来解释一下:

🍐 我们可以看上面的圈3,也就是第三点,假设我们可以将上面的代码改为后序遍历的话,在遍历完5的右子树后直接访问结点1的右子树,并没有回到结点2,也就没办法进行后序遍历。

🍐 怎么修改才可以进行后序遍历呢?首先要在遍历完右子树的时候能够回到根节点,并且要区分从左子树返回和从右子树返回,因为从左子树返回需要遍历右子树,从右子树返回不需要,详见下一章。


三、非递归后序遍历算法

代码在下方,详细分析:

🍅 首先,声明了一个堆栈stk用于存储遍历过程中的节点,并声明了三个指针变量:

  • cur用于存储当前正在处理的节点。
  • last_vist用于存储上一个被访问过的节点,用于区分当前节点是从左子树返回还是从右子树返回。
  • top用于存储堆栈顶部的节点。

🍅  然后,定义了一个循环,每次迭代中都会执行以下操作:

  • 如果cur 不为空,则将cur 压入堆栈中,并将cur 设置为其左子节点。
  • 否则,取出堆栈顶部的节点,并执行以下操作:
    • 如果堆栈顶部的节点有右子节点且没有被访问过,则将cur 设置为右子节点并继续遍历。
    • 否则,输出堆栈顶部的节点的数据,将最后访问的节点设置为堆栈顶部的节点,并将堆栈顶部的节点弹出堆栈。

🍅  循环条件为cur 不为空或者堆栈不为空。循环结束后,后序遍历完成。

与非递归前序、中序遍历的区别:

🍉 将之前代码入栈、出栈两个操作所提供的前序、中序遍历,扩展为入栈、取栈、出栈三个操作,其中入栈代表首次遍历到根节点,取栈代表从左子树返回准备遍历右子树,出栈代表右子树为空或右子树遍历完成。(添加取栈操作,遍历完根节点右子树后会回到根结点)

🍉 使用last_visit区分左子树返回,还是右子树返回。

void post_order_traverse_none(BiTree &bitree)
{
    // 将每一个非#元素看作根节点遍历一遍
    stack<BiTree> stk;
    BiTree cur = bitree;
    BiTree last_vist = NULL;// 用来区分返回根节点时,是从左子树返回的,还是右子树返回的
    BiTree top = NULL;
    while(cur || !stk.empty())
    {
        if(cur)// 如果cur不为空,将其压入栈中,并访问左子树
        {
            stk.push(cur);
            cur = cur->lchild;
        }
        else// 如果当前结点cur为空,取出栈顶元素
        { 
            top = stk.top();// 取出栈顶元素
            
            if(top->rchild && last_vist != top->rchild)// 如果栈顶结点的右子节点不为空,且没有被访问过
            {
                cur = top->rchild;// 如果cur指向的左子树为空,从栈顶取出根节点,将cur指向其右子树
            }
            else// 右子节点为空,且从右子树返回(一定是右子树返回,无论右子树是否为空)
            {
                // 否则,访问栈顶元素
                cout<< top->data<< "输出"<<endl;
                // 将栈顶元素设为最后访问的元素
                last_vist = top;
                // 将栈顶元素出栈
                stk.pop();
            }
        }
    }
}

四、加入圈子

层次遍历:从上到下,从左往右,一层层的遍历。

欢迎球球群交流,层次遍历

Python二叉树的遍历操作示例【前序遍历,中序遍历,后序遍历,层序遍历】
09-19
本文将深入探讨Python中的二叉树及其遍历方法,包括前序遍历、中序遍历后序遍历以及层序遍历。通过具体的代码示例,我们将更好地理解这些遍历方法的工作原理和应用场景。 #### 二、二叉树基础知识回顾 二叉树是由...
二叉树遍历之非递归算法
__七把刀__
07-22 3万+
在前一篇文章二叉树遍历递归算法二叉树遍历的递归算法做了总结,这篇文章就来对二叉树遍历的非递归算法做个汇总。还是与上一篇文章一样的顺序,一一汇总先序、中序、后序以及层序遍历的非递归算法。 1、先序遍历非递归算法先序遍历非递归访问,使用栈即可实现。先序遍历非递归访问在所有的遍历中算是最简单的了。主要思想就是先将根结点压入栈,然后根结点栈并访问根结点,而后依次将根结点的右孩子、左
先序遍历非递归
北街lhy的博客
03-25 7028
试写先序遍历非递归算法) 分析: 和中序遍历大同小异,唯一的差别在于每次先访问节点,在判断有没有左孩子,有则入栈,然后栈,往右走。直至栈空 代码如下: struct biTree {//树的结构体 char data; struct biTree *lchild; struct biTree *rchild; }; struct Stack {//栈的结构体 char* arr; //内存首地址 int len; //栈的容量 int top; //栈的下...
C++二叉树二叉树前序遍历、中序遍历后序遍历递归与非递归实现
最新发布
2202_75924820的博客
09-21 1785
前序遍历、中序遍历后序遍历的递归实现很简单,但是非递归迭代实现就有稍微有点难理解了。用了同一种思路,把树分为左路节点和左路节点的右子树,针对这三种遍历方式都适用。从根本上就是用循环模拟递归的过程。后序遍历相对前序和中序又稍微复杂,要考虑好访问节点的条件,避免死循环。多画图模拟迭代的过程更容易理解。
二叉树遍历之中序遍历算法非递归、递归)入门详解
sunnyoldboy博客
07-22 2万+
中序遍历的原理很简单,也就是把树根的访问放在中间。访问结点的次序是:“左—>根—>右”,也就是首先访问左子树,之后访问树根,最后访问右子树。对于左、右子树而言,其访问的次序依然是“左—>根—>右”。 不要小看这里的次序发生的改变,其实具体的实现方式比先序遍历也要麻烦了。 二叉树中序遍历先序遍历一样,都可以通过递归算法实现,也可以通过非递归算法实现。......
二叉树后序遍历非递归算法
热门推荐
二哈
06-03 4万+
1. 对于树的遍历我们最常用的三种遍历方法分别是前序遍历、中序遍历后序遍历,使用的方法是深度优先遍历树的每一个节点,这些遍历方法都是使用递归函数来进行实现的,在数据量大的情况下是比较低效的,原因在于系统帮助我们调用了一个栈并且做了诸如保护现场和恢复现场等复杂的操作,才使得遍历可以使用非常简单的代码来实现,所以我们可以模仿系统中调用的栈自己可以来写一下栈,模仿其中的过程就可以完成对于三种遍历算法的...
C语言实现非递归先序、中序、后序遍历
呵呵哒!的博客
11-22 5892
C语言实现非递归先序、中序、后序遍历
树的递归、非递归前序中序后序遍历实现,层序遍历,及树的Morris前序中序后序遍历实现
03-16
树的递归、非递归前序中序后序遍历实现,层序遍历,及树的Morris前序中序后序遍历实现。main函数有测试样例,测试样例是A B D F E C G H I 。注意空的地方是空格,前序建树。
非递归前序,中序,后序遍历二叉树(优化算法).rar_nooneyh_二叉树 非递归_前序 中序 后序_树遍历算法_遍历二叉树
09-14
对于二叉树非递归遍历算法,除了优化栈空间使用之外,还有助于理解和实现其他数据结构,如线索二叉树,以及在实际问题中的应用,如序列化和反序列化二叉树。这些算法在面试中也经常被问到,是每个程序员应该掌握的...
C#使用前序遍历、中序遍历后序遍历打印二叉树的方法
12-25
本文实例讲述了C#使用前序遍历、中序遍历后序遍历打印二叉树的方法。分享给大家供大家参考。具体实现方法如下: public class BinaryTreeNode { public BinaryTreeNode Left { get; set; } public ...
Python实现输入二叉树的先序和中序遍历,再输后序遍历操作示例
01-20
本文实例讲述了Python实现输入二叉树的先序和中序遍历,再输后序遍历操作。分享给大家供大家参考,具体如下: 实现一个功能:  输入:一颗二叉树的先序和中序遍历  输:后续遍历 思想: 先序遍历中,第一个元素...
二叉树前序遍历-非递归
qq_37080455的博客
06-29 3359
口诀:根左右知识点:栈栈是一种仅支持在表尾进行插入和删除操作的线性表,这一端被称为栈顶,另一端被称为栈底。元素入栈指的是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;元素栈指的是从一个栈删除元素又称作栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。思路:我们都知道递归,是不断调用自己,计算内部实现递归的时候,是将之前的父问题存储在栈中,先去计算子问题,等到子问题返回给父问题后再从栈中将父问题弹,继续运算父问题。因此能够递归解决的问题,我们似乎也可以用栈来试一试。根据前序遍历“根左右
二叉树后序遍历非递归算法
花间半盘棋
11-15 2万+
分析:后序遍历是三种遍历中最难的一种,后序遍历的特点为左右根,并且也需要借助一个栈来完成,如图,虚线表示p,q最开始的位置,用r指向最近访问过的结点。首先从根节点开始,沿着根的左孩子,将左孩子依次进行入栈。当D入栈之后,由于D没有右孩子,所以将D栈,此时r指向D。 D栈之后读栈顶元素B,p指向B,发现B有右孩子,然后将右孩子E入栈。 然后判断栈顶元素E,E没有右孩子了,所以将E栈,此时r指向E。 然后判断栈顶元素B,B的两个孩子都已经操作过了,所以B栈,p指向A,判断栈顶元素A,A有右孩子,右孩
二叉树遍历之后序遍历非递归、递归)入门详解
sunnyoldboy博客
07-28 3万+
二叉树后序遍历不如先序遍历简单,是相对最复杂的一种遍历方法。访问结点的次序是:“左—>右—>根”,也就是首先访问左子树,之后访问右子树,最后访问树根。对于左、右子树而言,其访问的次序依然是“左—>右—>根”。也就是说,对于每一棵子树,都是最后访问树根。...
非递归” 实现二叉树的“前序、中序、后序、层序”遍历
CYK_byte的博客
03-15 2713
相信学过或是了解过二叉树的朋友都知道,他的前序、中序、后序遍历使用递归法实现非常简单,那么如果使用非递归的方式来解,也就是使用迭代来解,你还能将他拿下么?实际上一点都不难,往下看~
非递归实现中序遍历
linminx的博客
04-03 3301
非递归实现中序遍历
数据结构与算法二叉树的层序、前序中序后序(递归、非递归)遍历
bigsai
08-21 9010
目录前言 层序遍历 前中后序遍历(递归) 前言 前面介绍了二叉排序树的构造和基本方法的实现。但是排序遍历也是比较重要的一环。所以笔者将前中后序.和层序遍历梳理一遍。 了解树的遍历,需要具有的只是储备有队列,递归,和栈。这里笔者都有进行过详细介绍,可以关注笔者数据结构与算法专栏。持续分享,共同学习。 层序遍历 层序遍历。听名字也知道是按层遍历。我们知道一个节点有左右节点。而每一层一层的遍...
更简单的非递归遍历二叉树
tkp2014的专栏
04-09 2694
解决二叉树的很多问题的方案都是基于对二叉树的遍历。遍历二叉树前序,中序,后序三大方法算是计算机科班学生必写代码了。其递归遍历是人人都能信手拈来,可是在手生时写非递归遍历恐非易事。正因为并非易事,所以网上现无数的介绍二叉树非递归遍历方法的文章。可是大家需要的真是那些非递归遍历代码和讲述吗?代码早在学数据结构时就看懂了,理解了,可为什么我们一而再再而三地忘记非递归遍历方法,却始终记住了递归遍历方
前序遍历、中序遍历后序遍历层序遍历详解附代码(数据结构C语言)
kim的博客
05-10 1万+
由于二叉树是递归定义的,显然,可以把二叉树遍历操作设计成递归算法。 从二叉树的定义可知,一棵二叉树由三部分组成:根结点,左子树和右子树。若规定D、L、R分别代表“访问根结点”、“遍历根结点的左子树”和“遍历根结点的右子树”,则共有6种组合:LDR、DLR、LRD、RDL、DRL和RLD,由于先遍历左子树和先遍历右子树在算法设计上没有本质区别,因此我们只讨论6种组合的前三种:DLR、LDR和LRD,根据遍历方法对访问根结点处理的位置不同,称这三种遍历方法分别为前序遍历(DLR)、中序遍历(L...
二叉树前序中序遍历后序遍历
08-23
前序遍历、中序遍历后序遍历是三种常用的二叉树遍历方式,用于...具体的算法可以用递归或者栈来实现,但核心思想就是利用前序中序遍历来恢复后序遍历的过程。如果需要,我可以提供一个简单的伪代码示例来帮助理解。
写文章

热门文章

  • 【014 关键字】一文彻底搞懂extern用法 70935
  • vi、vim的使用 查找关键字命令(一文彻底搞懂) 52920
  • 彻底解决Qt报错:无法定位程序输入点于动态链接库 47547
  • UART、SPI、I2C串行通讯协议解释 同步/异步 全双工/半双工通信含义 42428
  • STM32常见面试题 18233

分类专栏

  • QQ交流群: 100479172
  • 数据结构 65篇
  • C/C++ 55篇
  • linux基础 6篇
  • Makfile 2篇
  • linux裸机开发 2篇
  • uboot 8篇
  • linux驱动 55篇
  • Qt 5篇
  • 基于F1C200s的掌上电脑项目 15篇
  • 杂项 4篇
  • 虚拟机 4篇
  • 通讯协议 2篇
  • ESP8266 1篇

最新评论

  • 【014 关键字】一文彻底搞懂extern用法

    m0_49172457: 博主,您好,"delayms.h"、"lde.o"、"delay.o",在这里出现的有些突兀,请问,分别代表什么?

  • 正点原子 i.mx6ull裸板RGB屏 显示彩色图片 (单片机显示彩色图片通用)取模软件

    2301_79839360: 博主你好,请问定义数组时他提示太大了怎么办呢

  • 【014 关键字】一文彻底搞懂extern用法

    爱喝牛奶的红狐狸: 我想这里想问的是2.1 extern函数中方法2为什么不执行。我也没有执行。

  • nfs下载镜像报错File lookup fail、“TTTTTTTTTTTTTTT”

    Xiaotao_Zeng: 我滴神!!!

  • nfs下载镜像报错File lookup fail、“TTTTTTTTTTTTTTT”

    jaynara: 靠着这篇帖子弄好了,十分感谢

最新文章

  • Chrome多设备书签同步
  • 如何提高for循环的效率--兆易创新一面
  • 嵌入式软件中如何排查bug?
2023年183篇
2022年53篇
2021年1篇
2019年2篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kashine

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或 充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

玻璃钢生产厂家西安玻璃钢雕塑厂模块江苏大型主题商场美陈价格商丘玻璃钢卡通动物雕塑价格报价清远玻璃钢雕塑供应商家肇庆玻璃钢造型雕塑批发吉林步行街玻璃钢雕塑市场怒江卡通玻璃钢雕塑多少钱商业街玻璃钢雕塑定做厂家南宁玻璃钢艺术雕塑厂家浙江开业商场美陈采购黑龙江省玻璃钢雕塑企业达州玻璃钢海豚雕塑厂家亳州卡通玻璃钢雕塑湛江玻璃钢卡通雕塑玻璃钢雕塑摆放位置图广东季节性商场美陈批发河南中庭商场美陈供应温州水果玻璃钢雕塑生产厂家苏州玻璃钢雕塑卡通商场美陈 英文平顶山卡通玻璃钢雕塑萧山玻璃钢雕塑大象玻璃钢卡通雕塑设计合肥户内玻璃钢雕塑郑州标识标牌玻璃钢雕塑定做厂家玻璃钢玻璃钢雕塑厂家供应开封水景玻璃钢景观雕塑价格滁州卡通玻璃钢雕塑多少钱玻璃钢雕塑定制厂商党建文化玻璃钢卡通雕塑制作香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化