【二叉树】非递归实现前中后序遍历

news2024/11/14 10:36:08

目录

前言

算法思想

非递归实现前序遍历

过程分析

代码

非递归实现中序遍历

过程分析

代码

非递归实现后序遍历

过程分析

代码


前言

1)前序: 左子树 右子树

2)中序:左子树 右子树

3)后序:左子树  右子树

可以看出,这三种遍历方式的本质区别在于什么时候访问根节点,下面将介绍一种很厉害的思想,对于前中后序的非递归实现,它可以是通解。

算法思想

将二叉树分割成两部分:

1)左路节点

2)左路节点的右子树

它的每一棵子树也可以继续分为左路节点和左路节点的右子树。

实现这种算法,我们需要借助一种数据结构——栈,同时,为了存储数据我们还需要借助另一种数据结构——vector。

 下面,把这种算法转换成代码。

非递归实现前序遍历

过程分析

以这棵树为例:

1)所有左路节点进栈。

2)根据前序遍历的规则,所遍历到的结点都可以直接反问,即可以直接保存进vector中, 因为每个节点都可以是根。

3)左路结点遍历完后,出栈,在出栈时,如果它的右子树不为空,则将它的右子树的左路节点入栈,重复此操作,就可以遍历完左路节点对应        的右子树。

以上图为例:

所有左路节点进栈(面向屏幕右手边为栈顶):

stack:8  3  1 

vector:8  3  1

考虑1出栈,考虑到1的右子树为空,所以可以直接出栈。下面更新stack和vector。

stack:8  3

vector:8  3  1

考虑3出栈,考虑到3的右子树不为空,所以3出栈后,要把3的右子树的左路节点6和4依次入栈(结点3会有记录,不会找不到它的右树)。下面更新stack和vector。

stack:8  6  4

vector:8  3  1  6  4

考虑4出栈,考虑到4的右树为空,可以直接出栈。下面更新stack和vector。

stack:8  6 

vector:8  3  1  6  4

考虑6出栈,考虑到6的右子树不为空,所以出栈后,它的右子树的左路节点7要入栈。下面更新stack和vector。

stack:8  7

vector:8  3  1  6  4  7

考虑7出栈,考虑到7的右子树为空,所以可以直接出栈。下面更新stack和vector。

stack:8  

vector:8  3  1  6  4  7

考虑8出栈,考虑到8的右子树不为空,所以出栈后,它的右子树的左路节点10要入栈。下面更新stack和vector。

stack:10  

vector:8  3  1  6  4  7  10

考虑10出栈,考虑到10的右子树为空,所以可以直接出栈。下面更新stack和vector。

stack:空 

vector:8  3  1  6  4  7  10

此时,栈已空,所有结点均已访问完毕,前序遍历结束。

代码

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) 
    {
        stack<TreeNode*> st;
        vector<int> ret;
        TreeNode* cur = root;
        while(cur || !st.empty())
        {
            //所有左路节点入栈
            while(cur)
            {
                ret.push_back(cur->val);//遍历到的结点可直接访问
                st.push(cur);
                cur = cur->left;
            }
            TreeNode* top = st.top();
            //访问当前节点的右子树,如果不为空,会进入内层的while循环,把它的左路节点入栈
            //如果为空,则不会进入内存while循环
            cur = top->right;
            st.pop();
        }
        return ret;
    }
};

非递归实现中序遍历

过程分析

中序遍历和前序遍历尤为相似,它们的本质区别在于什么时候访问根结点。

1)所有左路节点进栈

2)左路节点进栈的同时不能去访问这些节点,因为根据中序遍历的规则,应该先访问左子树(这就是与前序遍历的区别)

3)所有左路节点都进栈以后,意味着最后一个左路节点的左子树(为空)已经访问完毕,可以访问根了,这时,弹出栈顶元素,并把它的值存       储进vector中,如果它有右子树,还应该把它的右子树的左路节点入栈。

代码

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) 
    {
        stack<TreeNode*> st;
        vector<int> v;
        TreeNode* cur = root;
        while(cur || !st.empty())
        {
            //左路节点入栈
            while(cur)
            {
                st.push(cur);
                cur = cur->left;
            }
            //退出循环后,意味着左路节点已访问,可以访问根了
            TreeNode* top = st.top();
            v.push_back(top->val);//访问根
            st.pop();
            cur = top->right;//左路节点的右子树如果非空,则让它的左路节点入栈
        }
        return v;
    }
};

非递归实现后序遍历

过程分析

后序遍历和前面的两种遍历方式其实差不多,都是把整棵树分为左路节点和左路节点的右子树。关键问题在于什么时候访问根。

1)左路节点进栈

2)左路节点进栈是不能直接去访问,根据后序遍历的规则,应该是先访问左子树,右子树再到根。

3)左路节点进栈完毕,意味着可以访问右子树了,如果左路节点的右子树不为空,就让它的左路节点进栈,如果为空,就可以访问根结点了

4)访问完左节点后,如果当前栈顶结点的右子树为空或者上一个访问的结点是它的右节点,满足这两个条件之一就可以访问当前节点了(根)

以上图为例 

所有左路节点进栈(面向屏幕右手边为栈顶):

stack:8  3  1 

vector:空

考虑1出栈,考虑到1的右子树为空,所以可以访问根即节点1,且1出栈。下面更新stack和vector。

stack:8  3

vector:1

考虑3出栈,考虑到3的右子树不为空,且上一个访问的节点不是它的右节点,所以3还不能访问,也还不可以出栈,要把3的右子树的左路节点6和4依次入栈 。下面更新stack和vector。

stack:8  3  6  4

vector:1

考虑4出栈,考虑到4的右树为空,可以直接出栈并访问该节点。下面更新stack和vector。

stack:8  3  6 

vector:1  4

考虑6出栈,考虑到6的右子树不为空且上一个访问的结点不是它的右节点,所以不能出栈,将它的右子树的左路节点7要入栈。下面更新stack和vector。

stack:8  3  6  7

vector:1  4

这里对节点6进行了第一次判断。

考虑7出栈,考虑到7的右子树为空,所以可以直接出栈并访问。下面更新stack和vector。

stack:8  3  6 

vector:1  4  7

考虑6出栈,考虑到6的右结点(7)为上一个访问的结点,所以可以访问6并将6弹出栈。下面更新stack和vector。

stack:8  3

vector:1  4  7  6

这里对节点6进行了第二次判断。可以看出,记录上一个访问的结点是有意义的。

考虑3出栈,考虑到3的右节点(6)为上一个访问的结点,所以可以访问3并弹出栈。下面更新stack和vector。

stack:8

vector:1  4  7  6  3

考虑8出栈,8的右子树不为空且它的右节点(10)不是上一个访问的结点,所以8还不可以出栈,应该将10入栈。下面更新stack和vector。

stack:8  10

vector:1  4  7  6  3

考虑10出栈,10的右树为空,可以访问,并弹出栈。下面更新stack和vector。

stack:8  

vector:1  4  7  6  3  10

考虑8出栈,考虑到上一个访问的结点10为8的右节点,所以8可以访问并弹出栈。下面更新stack和vector。

stack:空 

vector:1  4  7  6  3  10  8

此时,栈已空,所有结点均已访问完毕,后序遍历结束。

代码

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) 
    {
        stack<TreeNode*> st;
        vector<int> v;
        TreeNode* cur = root, *prev = nullptr;
        while(cur || !st.empty())
        {
            //左路节点入栈
            while(cur)
            {
                st.push(cur);
                cur = cur->left;
            }
            TreeNode* top = st.top();
            if(top->right == nullptr || prev == top->right)
            {
                //访问当前结点
                v.push_back(top->val);
                //更新prev
                prev = top;
                st.pop();
            }
            else
            {
                //右树的左路节点入栈
                cur = top->right;
            }
        }
        return v;
    }
};

完~

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

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

相关文章

Mysql 8.0 主从复制及读写分离搭建记录

前言 搭建参考&#xff1a;搭建Mysql主从复制 为什么要做主从复制&#xff1f; 做数据的热备&#xff0c;作为后备数据库&#xff0c;主数据库服务器故障后&#xff0c;可切换到从数据库继续工作&#xff0c;避免数据丢失。架构的扩展。业务量越来越大&#xff0c;I/O访问频…

PID控制中积分项目的理解,消除稳态误差的作用,表示着过去(PID积分控制)

1&#xff0c;消除稳态误差 积分项目是对于历史误差进行的累积&#xff0c;可以理解&#xff0c;系统的误差累积表示不断的在减少误差&#xff0c;最终消除误差&#xff0c;这个过程需要将误差进行累加&#xff0c;才可以真正知道误差的大小是多少&#xff0c;用最终累加的误差…

C++模板方法模式

文章目录 1. 定义抽象基类&#xff08;Abstract Class&#xff09;2. 实现具体子类&#xff08;Concrete Class&#xff09;3. 使用模板方法模板方法模式的优点模板方法模式的应用场景注意事项实现示例抽象类&#xff08;模板&#xff09;具体实现类客户端代码 总结 模板方法模…

期望薪资25K,新浪微博测试4轮面试,没想到过了。。

一面60min 1、离职原因 2、简单的算法题&#xff0c;就是我会什么让写什么&#xff1a; 冒泡排序&#xff0c;二分查找&#xff08;其实这么简单&#xff0c;我还是在指引下写出来的&#xff0c;自己实在太菜&#xff09; 3、简历问答&#xff08;随机抽几个点问&#xff0…

MySQL数据库语法(二)

一、数据库的创建 创建数据库CRATE DATABASE语法&#xff1a;CREATE DATABASE [IF NOT EXISTS]数据库名;功能&#xff1a;用给定的名字创建一个数据库如果数据库已经存在&#xff0c;发生一个错误。查看创建数据库&#xff1a;SHOW CREATE DATABASE <数据库名>&#xff…

Oracle数据库操作问题汇总

一、简介 Oracle Database&#xff0c;又名Oracle RDBMS&#xff0c;或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是世界上流行的关系数据库管理系统&#xff0c;系统可移植性好、使用方便、功能强&…

【数据结构】快速排序(详解)

目录 快速排序 历史&#xff1a; 基本思想&#xff1a; 主框架&#xff1a; 下面解释实现单次排序的几种版本&#xff1a; 1.Hoare版本 2. 挖坑法 3. 前后指针法 快速排序的实现包括递归与非递归&#xff1a; 1. 递归实现&#xff1a;&#xff08;即开头的基本框架&am…

工程机械比例阀电流采集方案——IPEhub2与IPEmotion APP

自从国家实施一带一路和新基建计划以来&#xff0c;工程机械的需求量呈现出快速增长的趋势。而关于工程机械&#xff0c;其比例阀的控制问题不容忽视。比例阀是一种新型的液压控制装置——在普通压力阀、流量阀和方向阀上&#xff0c;用比例电磁铁替代原有的控制部分&#xff0…

KEIL5鼠标右键查找定义或声明选项变灰色不可选

原因&#xff1a;我直接点的KEIL图标打开了昨天的工程 解决办法&#xff1a;关掉工程&#xff0c;重新从文件夹的路径打开 其他原因导致试试以下方法&#xff1a; 1.快捷键F12导航到目标位置 2.路径不能含有中文&#xff0c;改好后&#xff0c;shiftAltf12&#xff0c;更新搜索…

疑惑点:动作监听时this的含义:可以理解为接口的多态

全部代码&#xff1a; package test;import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Random;public class test3 extends JFrame implements ActionListener {JButton jb1 new JButton("你点我啊&am…

纽曼硬盘隐藏文件丢失怎么恢复?介绍几种有效的方法

纽曼硬盘作为存储设备中的佼佼者&#xff0c;以其高性能和稳定性受到了广大用户的青睐。然而&#xff0c;在使用过程中&#xff0c;有时我们可能会遇到一些意想不到的问题&#xff0c;比如隐藏文件的丢失。这对于依赖这些文件进行工作或生活的人来说无疑是一个巨大的困扰。那么…

香橙派AI Pro测评--ROS及激光SLAM

文章目录 前言一、外形与质感二、软件测评1. 系统界面2. ROS安装3. ROS节点测试4. SLAM算法测试 总结 前言 今天刚收到了官方寄来的“香橙派 AI Pro”开发板&#xff0c;这篇文章将会对香橙派 AI Pro的外形、质感、运行速度进行一个测评&#xff0c;然后我会在这个开发板上安装…

四步简单操作:轻松将iCloud照片恢复到相册

随着智能手机的普及&#xff0c;我们的生活中越来越多的照片存储在了云端&#xff0c;其中iCloud提供了便捷的照片备份和存储服务。但有时候&#xff0c;我们可能会不小心删除了在iCloud上的照片&#xff0c;或者想要将iCloud中的照片恢复到手机相册中。 在这篇文章中&#xf…

I.MX6ULL Linux 点灯实验理论及汇编点灯

系列文章目录 I.MX6ULL Linux C语言开发 I.MX6ULL Linux 点灯实验理论 系列文章目录一、I.MX6ULL GPIO二、I.MX6ULL IO 命名三、I.MX6ULL IO 复用四、I.MX6ULL IO 配置五、I.MX6ULL GPIO 配置六、I.MX6ULL GPIO 时钟使能七、硬件原理分析八、实验程序编写 一、I.MX6ULL GPIO 一…

flutter 的webview中touchstart和touchend 执行异常问题解决

效果 背景 使用flutter 调用webview内网页&#xff0c;网页内容是监听touchstart和 touchend&#xff0c;触发不同是事件&#xff0c;但是发现每次长按都 手指抬起后 才会执行 touchstart和touchend&#xff0c;满足不了我的需求&#xff0c;我的需求是当手指按下 立即执行touc…

aws emr启动standalone的flink集群

关键组件 Client&#xff0c;代码由客户端获取并做转换&#xff0c;之后提交给JobMangerJobManager&#xff0c;对作业进行中央调度管理&#xff0c;获取到要执行的作业后&#xff0c;会进一步处理转换&#xff0c;然后分发任务给众多的TaskManager。TaskManager&#xff0c;数…

解决mybatis/mybatis plus报错:Invalid bound statement (not found) 的方法汇总

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)问题&#xff0c;即在mybatis中dao接口与mapper配置文件在做映射绑定的时候接口与xml不匹配&#xff0c;要么是找不到&#xff0c;要么是找到了却匹配不到。 我的问题是项目没有把最新的方法x…

操作系统实验--终极逃课方法

找到图片里的这个路径下的文件 &#xff0c;结合当前题目名称&#xff0c;把文件内容全部删除&#xff0c;改为print print的内容为下图左下角的预期输出的内容

Databend 开源周报第 146 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 支持 Expressio…