二叉树前中后序遍历非递归实现C++

2023-05-16

前几天面试过程中面试官让手写一下二叉树后序遍历的非递归写法,当时没有写出来,本想着可能是因为面试太紧张的原因,才这么简单的题都没写出来,后来特地去研究了一下,发现二叉树的后序遍历非递归实现还真的没我想的那么简单,在此写个博客记录一下,顺便把前序和中序的非递归实现也写出来。

其实不管是前序、中序还是后序遍历,都只需要使用一个栈作为辅助来实现,其实现复杂度由到高分别为前序、中序、后序。下面按照前中后序遍历的顺序来记录一下非递归实现的思路和代码。

首先定义一下树节点的结构体:

struct TreeNode
{
    int data;
    TreeNode* left=nullptr;
    TreeNode* right=nullptr;
};

前序遍历(非递归)

思路

前序遍历的思路其实和DFS(深度优先搜索)是一样的,每次只需要从栈顶弹出一个节点,并将该节点的孩子节点压入栈,需要注意的就是,因为栈后入先出的特性,要先压右孩子再压左孩子才能满足前序遍历的访问顺序。

//前序遍历
void preOrderVisit(TreeNode* root)
{
    if(root==nullptr)
        return ;
    stack<TreeNode*> nodeStack;
    nodeStack.push(root);
    while(!nodeStack.empty())
    {
        cout<<nodeStack.top()->data<<" ";
        TreeNode* temp=nodeStack.top();
        nodeStack.pop();
        //前序遍历一定要注意先压入当前节点的右孩子,因为栈是先进后出的结构
        if(temp->right)
            nodeStack.push(temp->right);
        if(temp->left)
            nodeStack.push(temp->left);
    }
}

中序遍历(非递归)

思路

如果使用栈来实现中序遍历,如果栈顶的当前节点没有被访问,那么就需要不断地把栈顶节点的左孩子压入栈,反复循环直到左孩子为空;那么这样就需要解决一个问题:如何判断栈顶的当前节点有没有被访问过呢?(即要判断是第一次访问还是经过回溯弹栈之后再次访问);这里的解决方法是用一个bool类型的变量traceback来标记当前节点是否是通过回溯访问的,traceback初始化为false,即标记为第一次访问而不是回溯访问。关键的是要在当前节点右孩子为空时,即回溯返回,将traceback置为true,当前栈顶的元素是回溯返回的再次方位,这样就不会将其左孩子反复压入栈;如果右孩子不为空,将其右孩子压入栈,并将traceback置为false,

//中序遍历
void inOrderVisit(TreeNode* root)
{
    if(root==nullptr)
        return ;
    stack<TreeNode*> nodeStack;

    bool traceback=false;   //用来标记当前节点是否是回溯访问的

    nodeStack.push(root);
    
    while(!nodeStack.empty())
    {
        //如果不是回溯访问,那么不断将栈顶节点的左孩子压入栈,直到叶子节点
        while(nodeStack.top()->left!=nullptr&&!traceback)
            nodeStack.push(nodeStack.top()->left);

        TreeNode* temp=nodeStack.top();
        cout<<temp->data<<" ";
        nodeStack.pop();

        if(temp->right)
        {
            traceback=false;    //若存在右孩子则将其压入栈,并标记为不是回溯访问
            nodeStack.push(temp->right);
        }
        else
            traceback=true;     //若不存在右孩子,则标记为回溯返回
    }
}

后序遍历非递归实现

思路

后序遍历的实现思路和中序遍历有相似的地方,都需要判断当前栈顶节点是否是回溯返回,同样的使用一个bool类型的变量来标记是否是回溯返回。不一样的地方就是父节点不能先于右孩子弹出栈,而是要继续留在栈中,这就需要判断当前节点(栈顶元素)的右孩子是否被访问过(和之前判断当前节点是否被访问过不一样,这里是要判断当前节点的右孩子是否被访问过);对此可以使用一个指针用来指向上一次出栈的节点,这样在访问当前节点的时候就可以判断上一次出栈的节点是不是当前节点的右孩子;如果是它的右孩子则说明当前节点的右孩子已经被访问过了,就不需要将右孩子压栈了;否则需要将当前节点的右孩子压入栈

//后序遍历
void postOrderVisit(TreeNode* root)
{
    if(root==nullptr)
        return ;
    stack<TreeNode*> nodeStack;
    TreeNode* prev=nullptr; //记录上一次从栈中弹出的节点
    bool traceback=false;   //用来标记当前节点是否是回溯访问的

    nodeStack.push(root);
    while(!nodeStack.empty())
    {
        //如果不是回溯访问,则一直讲栈顶节点的左孩子压入栈
        while(nodeStack.top()->left!=nullptr&&!traceback)
            nodeStack.push(nodeStack.top()->left);

        //如果当前节点没有右孩子或者右孩子已经被访问则弹出该节点
        if(nodeStack.top()->right==nullptr||prev==nodeStack.top()->right)
        {
            cout<<nodeStack.top()->data<<" ";
            prev=nodeStack.top();
            nodeStack.pop();
            traceback=true;     //标记回溯返回
        }
        else if(nodeStack.top()->right!=nullptr)
        {
            nodeStack.push(nodeStack.top()->right);
            traceback=false;    //标记非回溯返回
        }
    }
}

验证

这里自己构建一颗简单的树来验证前面写的前中后序遍历的实现是否正确:
构造一颗二叉树用于验证

验证代码

int main()
{
    /***************构建二叉树*****************/
    TreeNode* n1=new TreeNode;
    TreeNode* n2=new TreeNode;
    TreeNode* n3=new TreeNode;
    TreeNode* n4=new TreeNode;
    TreeNode* n5=new TreeNode;
    TreeNode* n6=new TreeNode;
    TreeNode* n7=new TreeNode;
    n1->data=1;n1->left=n2;n1->right=n3;
    n2->data=2;n2->left=n4;n2->right=n5;
    n3->data=3;n3->left=n6;n3->right=n7;
    n4->data=4;n5->data=5;n6->data=6;n7->data=7;
    /****************************************/

    //前序遍历
    cout<<"preOrderVisit: ";
    preOrderVisit(n1);
    cout<<endl;

    //中序遍历
    cout<<"midOrderVisit: ";
    inOrderVisit(n1);
    cout<<endl;

    //后序遍历
    cout<<"postOrderVisit: ";
    postOrderVisit(n1);
    cout<<endl;
    return 0;
}

程序输出

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

二叉树前中后序遍历非递归实现C++ 的相关文章

  • Centos7 java服务开机自启动

    1 在 etc systemd system 目录下 新建脚本 cd span class token operator span usr span class token operator span lib span class toke
  • 我使用过的linux命令之strings

    strings命令用于输出文件中可打印的字符串 不论文件是普通文本 xff0c 还是可执行文件 xff0c 任何文件都可以 最常用的选项 xff1a a 扫描整个文件的任何段 xff0c 这是strings的默认行为 xff0c 但是这种默
  • HashMap的工作原理

    HashMap主要是用来处理键值对数据 xff0c 随着JDK版本的更新 xff0c JDK1 8对HashMap的底层也做了一些优化 xff0c HashMap是基于哈希表对Map接口的实现类 xff0c 它的特点是访问数据速度快 xff
  • 如何配置终端代理apt 代理

    1 临时用代理 xff0c 直接在终端里export代理 export http proxy 61 http 127 0 0 1 7890 export https proxy 61 http 127 0 0 1 7890 2 在 etc
  • ssh修改连接端口,以及修改端口之后连接不上的问题

    SSh服务配置文件路径一般都是在 etc ssh这个目录下面 sshd config 这个文件 使用VI vim编辑器 xff0c 打开sshd config这个文件 xff0c 搜索找到 port字段 去掉 xff0c 修改port 后面
  • FreeRTOS原理剖析:任务的创建

    1 任务创建API函数 任务的最基本功能是任务管理 xff0c 任务管理中最基本操作是任务的创建和删除 对于任务的创建和删除 xff0c 由于篇幅有点长 xff0c 分两篇分别讲解 在FreeRTOS中任务的创建函数如下 xff1a 函数描
  • @xmlAttribute等注解它的用处?

    用的是jdk自带的javax xml bind JAXBContext将对象和xml字符串进行相互转换 如果对要生成的 xml 格式有点些许的限制 xff0c 就会对生成xml的对象就需要进行些许控制 xff0c 控制对象的一个最可行的办法
  • C/C++ 分支预测(likely unlikely)

    看一些代码时 xff0c 会遇到likely unlikely 查了查网上的资料 xff0c 结合自己的理解记录一下 1 一些概念 指令周期 是指执行一条指令所需要的时间 xff0c 一般由若干个机器周期组成 xff0c 是从取指令 分析指
  • Vnc viewer与windows之间的复制粘贴

    用VNC连接到Linux之后 xff0c 最纠结的问题就是无法复制粘贴 其实很简单 xff0c 在Linux里面 xff0c 打开一个终端 xff0c 然后输入命令 xff1a vncconfig 之后 xff0c 会弹出一个窗口 不要关闭
  • Android studio 添加多语言支持

    环境 xff1a Android studio 3 2 执行步骤 xff1a 一 生成对应语言文件夹 选中你的工程 gt res gt 右键点击new gt 选中Android resource directory Available qu
  • VNC 远程环境搭建教程

    最近因项目需要使用到 VNC 远程工具 xff0c 因此记录使用过程 一 在 VNC 官网下载 VNC 服务端和客户端安装包 进入下载页面 二 注册 VNC 官网账号 三 在本地安装 VNC 客户端 xff0c 被远程电脑安装 VNC 服务
  • Ubuntu桌面出现Accept clipboard from viewers,Send clipboard to viewers,Send primary selection to vi等三行错误时

    如上图的错误时 1 输入以下神秘代码 sudo apt get install gnome core2 重启vnc服务3 若还不行 xff0c 则修改xstartup脚本 方法见下链接第五部分 修改xstartup
  • Python+ADB实现Android手机QQ自动点赞

    1 前言 前段时间看了些爬虫的知识 xff0c 然后又看到selenium xff0c Appium xff0c 在Appium环境设置过程中 xff0c 意外地看到这个帖子adb命令模拟按键事件 KeyCode xff0c 然后结合相关搜
  • Go语言汇编入门

    虽然在前面的文章中 xff0c 分析代码已经接触了一些Go语言的汇编代码的注解 xff0c 比如在slice和Go语言笔记以及以后的文章中都会使用到Go汇编 本章主要讲解Go汇编大致流程的框架 xff0c 对于刚接触Go汇编理解Go函数栈是
  • Holistic++ Scene Understanding论文翻译解析笔记

    Holistic 43 43 Scene Understanding 摘要 我们提出了一个新的3D整体场景理解问题 xff0c 它l共同解决了单视角图片的两个问题 xff08 1 xff09 整体场景的语义分析和重建 xff08 2 3D人
  • windows server 2012R2制作qcow2镜像

    一 环境准备 xff1a 1 windows server 2012R2的iso镜像 2 物理机一台 xff1a 要求支持硬件虚拟化 xff0c 并且装好了centos系统 xff0c 在windows上安装vmware 然后在vmware
  • VNC无法连接,出现“TOO MANY SECURITY FAILURES”(安全故障太多)

    通过VNC VIEWER远程管理 xff0c 连接的时候报错 too many security failures 这是因为VNC的黑名单机制 xff0c 用来保护你的服务器 如果有人暴力破解 xff0c 将会触发VNC的黑名单机制 处理方
  • java.sql.SQLException: #HY000

    勾选自动递增 将 type类型改成int xff0c binyint是boolean xff0c 类型是1 xff0c 2 xff0c 3 xff0c 4 xff0c 不是true false 就好了
  • jqgrid表格高度宽度设置

    jqgrid表格高度宽度设置 问题说明 gt 页面上使用上面搜索框 xff0c 下面是jqgrid表格形式 xff0c 总是出现 xff0c grid表格加载宽度 高度问题 本文通过主要解决表格高度宽度变化适应的问题 grid宽度 1 修改

随机推荐

  • 梯度下降法及matlab实现

    个人博客文章链接 xff1a http www huqj top article id 61 162 梯度下降法 xff08 gradient descent xff09 xff0c 是机器学习中最常用的参数调优算法 xff0c 所谓梯度下
  • 命令执行判断($?:命令回传值、&&、||)

    1 概述 当在linux中输入命令时 xff0c 命令可能成功也可能失败 xff0c 此时可以通过命令回传值来判断 xff08 符号 xff1a xff09 xff0c 命令回传值可以和 amp amp 与 一起使用 2 符号 amp am
  • LXC/KVM虚拟化基本概念

    1 LXC 其名称来自Linux软件容器 xff08 Linux Containers xff09 的缩写 一种操作系统层虚拟化 xff08 Operating system level virtualization xff09 技术 xf
  • 解决VNC连接安了Ubuntu MATE系统的树莓派3b时出现灰屏的问题

    1 xff09 首先安装vncserver服务 xff08 这一步有没有用我也不知道 xff0c 一般人都是装的tightvncserver 当然 xff0c 我也装了 xff09 sudo apt get install vnc4serv
  • Ubuntu18.04安装CUDA10、CUDNN

    上篇记录了Ubuntu下安装INVIDIA显卡驱动的方法 xff0c 尽管可以选择CUDA自带的驱动 xff0c 但为了避免不必要的问题 xff0c 尽量单独安装 如果没有单独安装驱动 xff0c 建议多找几篇博客 xff0c 对比来看 x
  • IDEA mavne项目转gradle项目

    文章目录 前言一 配置gradle二 将mavne项目转换为gradle1 找到项目根目录2 执行命令转换3 重启项目 配置IDEA 的 gradle4 转换后的样子 总结 前言 不知道小伙伴有没有遇到过这个问题 就是由于项目是用maven
  • 2016,再见 2017,还请多多指教

    先来一个象征意义上的序 今天是2017 01 01 新年的第一天 昨天适合总结 今天适合制作新年计划 昨天没做总结 于是今天总结和新年计划一起来吧 充满回忆的2016 昨天在驾校练车练了一天 倒库终于能倒进去了 回到住处已经下午5点 买了路
  • 3. 云计算的落地实践(下)

    本章讲解知识点 云计算如何落地实践 ISO镜像文件 创建虚拟机入门 创建数据节点 配置 VMWare创建虚拟机三种网络模式 1 云计算的落地实践 上一章我们讲了云计算的业界实践 即 搭建IaaS后 用于创建虚拟机 在虚拟机上部署PaaS 用
  • deepin15.8配置深度显卡驱动

    以deepin15 8为例 xff0c 电脑为联想的y7000 刚开始以网上下载 run文件的方式进行安装显卡驱动 xff0c 后来在下载cmake等一下工具的时候 xff0c 总会提示与显卡驱动某个模块版本冲突 xff0c 所以索性放弃了
  • 关于调用第三方接口时传递参数是File类型的解决方式

    最近在做一个项目 xff0c 需要频繁的调用第三方的接口 xff0c 本以为都是基本的数据类型 xff0c 没想到需要传一个文件类型的参数 xff0c 我想着调用接口的时候直接用文件流把文件写到connection不就行了 xff0c 这就
  • C#中?、?.、? ?、? ?=的用法和说明

    一 可空类型修饰符 xff1f 引用类型能用空引用来表示一个不存在的值 xff0c 但是值类型不能 例如 xff1a string str 61 null int i 61 null 编译报错 为了使值类型也能使用可空类型 xff0c 就可
  • TortorliseGit(小乌龟)创建删除(远程和本地)分支

    以下两篇文章分别为删除和创建 1 使用TortorliseGit 小乌龟 删除本地分支 xff0c 远程分支 2 使用TortoiseGit操作分支的创建与合并
  • UML类图的几种关系浅析

    类图中的主要关系有如下几种 关联关系 聚合关系 组合关系依赖关系泛化关系细化关系 注 xff1a 以下图片均来自网络 xff0c 侵删 1 关联关系 关联关系是类与类之间的连接 xff0c 表示一类对象与另一类对象之间有联系 xff0c 通
  • 关于c#创建界面的几种方式

    c 创建界面有很多种方式 xff0c 下面列举5中创建界面的方式 1 windows窗体 xff0c 这种窗体设计界面是最简单的一种 这种可以直接从工具箱拿出组件进行使用 xff0c 能够很好的设计界面 2 用户控件类 3 组件类 4 窗口
  • .ova文件转换成.ovf和.vmdk格式

    一 准备工具 xff1a 下载软件 xff1a OVFTool x64 下载地址 xff1a https pan baidu com s 1YDtHh0 OnK0Lm5C4KoF4 w 二 安装后 xff0c 去安装路径下 xff0c 按住
  • 【Word】如何在数学公式同一行末尾填写编号

    使用word插入公式框后 xff0c 在公式框中打完公式的末尾处 xff08 依旧在框内 xff09 加上 xff08 编号 xff09 xff0c 然后回车即可 xff01 xff01 超神器 xff01 再也不用手动空格啦 输入公式序号
  • 使用Xmanager软件远程调用图形化(可视化)安装Oracle数据库

    安装Oracle xff0c 使用调用图形化界面进行安装 xff0c 此次不能使用VNC远程到服务器本地进行图形化安装 xff0c 只能远程调用图形化进行本地安装 xff0c 方法如下 xff1a 一 Linux系统安装所需要的依赖组 xf
  • js中怎么删除对象的某个key值?js 遍历数组,有用!!

    参考 xff1a https blog csdn net denghaolinzy article details 87913561 formThead cate false id true out trade no true produc
  • UDP数据包的延迟及丢包检测(C++)

    摘要 本文记录通过数据报套接字来检测UDP数据包的延迟和丢包的思路和简单的代码实现 思路 UDP协议及用户数据报协议在传输层提供了无连接 不可靠的传输服务 xff0c 端到端的延迟以及丢包率是反应当前网络环境好坏的重要评价标准 Ping检测
  • 二叉树前中后序遍历非递归实现C++

    前几天面试过程中面试官让手写一下二叉树后序遍历的非递归写法 xff0c 当时没有写出来 xff0c 本想着可能是因为面试太紧张的原因 xff0c 才这么简单的题都没写出来 xff0c 后来特地去研究了一下 xff0c 发现二叉树的后序遍历非