二叉树的前中后序遍历(非递归迭代实现)

news2024/11/15 18:42:42

1.二叉树的前序遍历

1.1 思路分析

前序遍历的顺序是根-左子树-右子树,所以首先从根节点开始,顺着访问左子树:1、2、4。此时,还剩下节点1、节点2、节点3的右子树没有访问。后面倒着访问节点1、2、4的右子树就行了。所以非递归的前序遍历是这样处理的:把一棵二叉树分为两个部分:1、左路节点;2、左路节点的右子树。如下图所示:

对于每一棵右子树,也是同样划分为这两个部分进行处理。

如何倒着取处理左路节点的右子树?我们可以借助栈来处理左路节点的右子树。以上图中的树为例,从根节点1开始,依次访问左路节点1、2、4。在访问的过程中,将左路节点节点放到要返回的vector中,同时把左路节点放到栈里(根据栈的性质,就可以倒着往上依次访问左路节点的右子树)。如下图所示:

接着,依次取栈的栈顶元素,同时访问栈顶元素的右子树。

1、栈顶元素4出栈,再访问节点4的右子树,将节点4的右子树入栈,由于该节点的右子树为空,则结束;

2、接着,栈顶元素2出栈,再访问节点2的右子树,此时节点2的右子树(节点5)入栈,同时将节点5放入到vector中;

3、栈顶元素节点5出栈,再访问节点5的右子树,将节点5的右子树入栈,由于该节点的右子树为空,则结束;

4、栈顶元素1出栈,再访问节点1的右子树,与之前的处理一样,将节点1的右子树分为左路节点和和左路节点的右子树,依次访问节点3、6;左路节点3、节点6入栈,同时节点3、6放入vector中;

5、栈顶元素节点6出栈,再访问节点6的右子树,将节点6的右子树入栈,由于该节点的右子树为空,则结束;

6、栈顶元素节点3出栈,再访问节点3的右子树节点7,节点7入栈并放入vector中;

7、栈顶元素节点7出栈,再访问节点7的右子树,再访问节点7的右子树,将节点7的右子树入栈,由于该节点的右子树为空,则结束。

具体如下图所示:

1.2 代码实现

vector<int> preorderTraversal(TreeNode* root)
{
    vector<int> ret;
    stack<TreeNode*> st;

    TreeNode*cur=root;
    while(cur||!st.empty())
    {
        while(cur)
        {
            //将二叉树的左路节点入栈,并同时放入数组中
            st.push(cur);
            ret.push_back(cur->val);
            cur=cur->left;
        }

        //栈顶元素出栈,同时该元素的右子树节点入栈
        TreeNode* top=st.top();
        st.pop();
        cur=top->right;
     }
     return ret;
}

2.二叉树的中序遍历

2.1 思路分析

二叉树的中序遍历的思路与前序遍历的思路差不多,中序遍历还是将二叉树分为左路节点和右子树,如下图所示。

在前序遍历中,我们将左路节点入栈时,就把他们放入到要返回的vector中了,这样就符合前序遍历的顺序了。中序遍历的顺序是:左子树->根节点->右子树

1、我们将左路节点入栈,但是不放入到vector中;一直走到节点4的左子树为空,停止入栈,此时可以认为节点4的左子树是空,已经遍历过了;

2、栈中的节点依次出栈(当从栈中取出节点时,就意味着该节点的左子树已经被访问过了),第一个出栈的是栈顶元素节点4,并放入到vector中;访问完节点4的左子树和节点4本身(根节点),此时就该访问节点4的右节点,为空;

3、接着,栈顶元素节点2出栈,并放入到vector中;然后访问节点2的右子树节点5,与第一步一样,节点5入栈(由于节点5左右子树均为空,故只有节点5独自入栈);

4、栈顶元素节点5出栈,并放入到vector中;此时访问节点5的右子树,为空;

5、栈顶元素节点1出栈,并放入到vector中;然后访问节点1的右子树,与第一步一样,将节点1的右子树的左路节点3、6依次入栈;

6、栈顶元素节点6出栈,并放到vector中;访问完节点6的左子树和节点6本身(根节点),此时就该访问节点6的右节点,为空;

7、栈顶元素节点3出栈,并放到vector中;然后访问节点3的右子树,与第一步一样,节点3的右子树节点7入栈;

8、栈顶元素节点7出栈,并放到vector中;此时访问节点7的右子树,为空,则结束。

2.2 代码实现

vector<int> inorderTraversal(TreeNode* root)
{
    vector<int> ret;
    stack<TreeNode*> st;

    TreeNode*cur=root;
    while(cur||!st.empty())
    {
        while(cur)
        {
            //将二叉树的左路节点入栈
            st.push(cur);
            cur=cur->left;
        }

        //栈顶元素出栈,同时将该栈顶元素放入数组中
        TreeNode* top=st.top();
        st.pop();
        ret.push_back(top->val);
        cur=top->right;
     }
     return ret;
}

3.二叉树的后序遍历

3.1 思路分析

后续遍历的顺序是左子树->右子树->根,对于后续遍历依然是将二叉树分为左路节点和右子树,如下图。

对于栈顶元素可以像中序遍历那样直接将栈顶元素放入vector中,然后pop掉吗?答案是不可以,因为对于中序遍历,从栈中取出一个左路节点时,就意味着该节点的左子树已经被访问过了,接着就该访问根节点了,将栈顶元素放入vector中,然后pop掉就相当于访问根节点。后续遍历不能这样做,因为后续遍历要在访问完右子树之后再去访问根节点。

对于后续遍历来说,能不能直接将栈顶元素直接pop掉,然后放入到数组vector中,需要看情况。以下两种情况可以直接将栈顶元素pop掉,然后放入到数组vector中。1、栈顶元素的右子树为空;(如果栈顶元素的右子树不为空,要先访问右子树,再访问根节点。) 2、右子树已经被访问过了。那么问题来了,右子树为空很好判断,但是如何判断某个节点的右子树是否已经被访问过了呢?我们可以定义一个prev指针,每次访问完一个节点并出栈之后,将这个节点的指针赋值为prev指针,就可以通过prev来判断,该被访问过的节点是否为当前栈顶节点的右子树。

具体过程如下图所示。

3.2 代码实现

vector<int> postorderTraversal(TreeNode* root)
{
    vector<int> ret;
    stack<TreeNode*> st;
    TreeNode*prev=nullptr;

    TreeNode* cur=root;
    while(cur||!st.empty())
    {
        //遍历二叉树的左路节点并入栈
        while(cur)
        {
            st.push(cur);
            cur=cur->left;
        }

        //取到栈顶元素时,栈顶节点的左子树已经被访问过了
        TreeNode*top=st.top();

        //如果该栈顶元素的右子树为空,或者该栈顶元素的右子树已经被访问过了,
        //则访问该栈顶元素
        if(top->right==nullptr||top->right==prev)
        {
            st.pop();
            ret.push_back(top->val);
            //记录prev
            prev=top;
        }
        else
        {
            //否则就先处理右子树
            cur=top->right;
        }
    }
    return ret;
}

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

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

相关文章

解决跨境电商平台账号无法访问的常见问题

跨境电商的迅猛发展&#xff0c;越来越多的卖家选择在全球各大电商平台如亚马逊、eBay等进行商品销售。然而&#xff0c;在实际运营过程中&#xff0c;卖家经常会遇到账号无法访问、应用打不开等问题&#xff0c;导致业务受阻。本文将针对这些问题进行详细分析&#xff0c;并提…

【医疗影像】THE BEER-LAMBERT LAW

吸光度 A l o g 10 ( I 0 I ) A log_{10}(\frac{I_0} I) Alog10​(II0​​) 如果您了解光谱仪如何工作&#xff0c;您就会知道它使一系列波长的光穿过物质溶液&#xff08;样品cell&#xff09;&#xff0c;同样地&#xff0c;也会穿过溶剂&#xff08;参考cell&#xff09; …

信息安全工程师(5)域名与域名解析

一、域名 1. 定义与功能 域名&#xff08;Domain Name&#xff09;是互联网上用于标识网站或服务器地址的名称&#xff0c;由一串由点分隔的字符组成&#xff0c;如“example.com”。域名的主要功能是提供一种便于记忆和输入的地址形式&#xff0c;以代替难以记忆的IP地址。域名…

【Axure原型】B端系统登录注册页设计成这样,就不用跟小孩一桌了

前言 在B端后台中&#xff0c;登录注册页这个东西&#xff0c;因为感觉很简单&#xff0c;所以经常不被产品经理们重视。但是登录注册页作为一个后台系统的门面&#xff0c;直接影响用户第一印象&#xff0c;又是非常重要的存在。 登录注册页的价值 B端系统登录注册页是用户…

浸没边界法空间精度相关的论文的阅读笔记

Convergence proof of the velocity field for a stokes flow immersed boundary method https://doi.org/10.1002/cpa.20233 研究对象的选取 他这里为什么能够选取一个周期性边界的流场啊&#xff1f;为什么不是狄利克雷边界或者诺伊曼边界&#xff1f; 方形流场的边界值 …

【Linux】精通GDB:打造你的Linux调试超能力

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;C从入门到精通 目录 一&#xff1a; &#x1f525; 什么是GDB / CGDB 二&#xff1a; &#x1f525; CGDB的安装 &#x1f34a; Linux-centos 三&#xff1a; &#x1f525; cgdb的使用背景 &#…

vmware,centos8(虚拟机) 的安装

安装vmware 点击下方网址 虚拟机安装地址https://www1.msc23.cn/vm/?bd_vid8829610582362807097选择VMware17 打开文件所在地&#xff0c;双击安装 同意条款 选择安装位置 不将VMware配置到环境变量path 不检查更新,不加入客户体验 创建桌面快捷方式 开始安装 安装完成…

47.面向对象综合训练-汽车

//题目需求&#xff1a;定义数组存储3个汽车对象 //汽车的属性&#xff1a;品牌&#xff0c;价格&#xff0c;颜色 //创建三个汽车对象&#xff0c;数据通过键盘录入而来&#xff0c;并把数据存入到数组当中 1.标准的JavaBean类 public class Car {private String brand;//品…

基于yolov8的谢韦尔钢材缺陷检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的谢韦尔钢材缺陷检测系统&#xff0c;充分利用了YOLOv8算法的高效性和准确性&#xff0c;为钢材表面缺陷检测提供了先进的解决方案。该系统通过深度学习技术&#xff0c;特别是YOLOv8这一实时目标检测算法&#xff0c;能够快速、准确地识别钢材表面的…

【Linux进程】Linux Shell编程实战:构建简易脚本示例与技巧详解

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;Linux “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;暂无 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀Linux进程 &#x1f4d2;1. 获取输入&…

《Nginx核心技术》第17章:使用自签CA配置HTTPS加密反向代理访问

作者&#xff1a;冰河 星球&#xff1a;http://m6z.cn/6aeFbs 博客&#xff1a;https://binghe.gitcode.host 文章汇总&#xff1a;https://binghe.gitcode.host/md/all/all.html 星球项目地址&#xff1a;https://binghe.gitcode.host/md/zsxq/introduce.html 沉淀&#xff0c…

事务方法中存在远程调用

1. 背景 在实现下单的方法中&#xff0c;首先通过远程调用查询了地址簿信息和服务信息等&#xff0c;然后将这些信息和前端传入的预约时间、服务项等信息封装到order实体类&#xff0c;然后调用Mybatis-Plus提供的ServiceImpl类的save方法&#xff0c;向数据库表中插入数据。 …

基于51单片机的220V交流数字电流表proteus仿真

地址&#xff1a;https://pan.baidu.com/s/1QmpPLvDTuW7QG7P-JCLPPg 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectron…

直流无刷电机霍尔线序自学习解释

直流无刷电机霍尔线序自学习 步骤详解 1. 初始连接 连接电机的三相线&#xff1a;A、B、C。连接霍尔传感器线&#xff1a;HA、HB、HC。 2. 输入电压组合与霍尔信号记录 电机的电压输入组合和霍尔信号记录是电机控制系统中至关重要的一部分&#xff0c;它们决定了电机的运转…

CODESYS标准化编程之输入输出映射

在介绍输入输出映射之前大家需要了解开关量防抖滤波功能块,相关链接如下: 开关量防抖滤波器(梯形图和SCL源代码)_开关量输入滤波程序-CSDN博客文章浏览阅读724次。本文介绍了开关量防抖滤波器的原理和应用,包括梯形图和SCL源代码的实现。通过防抖滤波功能块,可以有效滤除低…

2024年9月15日

30min 1.配置数据库得到数据表 2.连接数据库 英语六级 遇到了报错

YOLOv10改进系列,YOLOv10颈部网络SPPF替换为FocalModulation

摘要 焦点调制网络(简称FocalNets),其中自注意力(SA)完全由焦点调制模块取代,用于在视觉中建模标记交互。焦点调制包括三个组件:(i)焦点情境化,通过一堆深度卷积层实现,从短到长范围编码视觉上下文,(ii)门控聚合,选择性地将上下文聚集到每个查询标记的调制器中…

OpenCV和Tesseract OCR识别复杂验证码喽~~

目录 代码实现思路 流程&#xff1a; 主要流程&#xff1a; 整体代码 效果展示 原图 处理之后的图 总结 流程图 代码实现思路 使用 OpenCV 进行图像预处理&#xff0c;并通过 Tesseract OCR 来识别验证码中的字符。以下是其实现思路的详细讲解&#xff1a; 流程&…

人工免疫算法(AIS算法)求解实例---旅行商问题 (TSP)

目录 一、采用AIS求解 TSP二、 旅行商问题2.1 实际例子&#xff1a;求解 6 个城市的 TSP2.2 **求解该问题的代码**2.3 代码运行过程截屏2.4 代码运行结果截屏&#xff08;后续和其他算法进行对比&#xff09; 三、 如何修改代码&#xff1f;3.1 减少城市坐标&#xff0c;如下&a…

【AIGC cosplay】让大模型扮演求职者,我当hr来面试

【AIGC cosplay】让大模型扮演求职者&#xff0c;我当hr来面试 背景前摇&#xff08;省流可不看&#xff09;&#xff1a; 工位附近有几位人力资源的老师&#xff0c;我发现他们这个职位可以看到好多别人的简历&#xff0c;还能跟好多人聊天&#xff0c;完事了跟同事悄悄吐槽…