二叉树的遍历之迭代遍历

news2024/11/19 23:36:26

前言:在学习二叉树的时候我们基本上已经了解过二叉树的三种遍历,对于这三种遍历,我们采用递归的思路,很简单的就能实现,那么如何用迭代的方法去解决问题?

我们首先来看第一个:

前序遍历

144. 二叉树的前序遍历 - 力扣(LeetCode)

前序遍历就是以 根 左子树 右子树的顺序遍历二叉树。呢么如何用得带去解决问题呢?

首先我们知道函数递归就是函数一层层的调用自己,本质上是以一种先进后退的思路,而这与栈的特性一致,因此函数递归,我们可以用调用堆栈来看,就是栈的思路。

现在我们解决问题,既然不用递归,但还是可以用到解决问题的本质思路,就是用栈去解决问题:

例图:

 我们就用这个图做演示:

大致解题思路:由于二叉树遍历一直往下走,无法回头,我们需要提供一个栈,假设我们每次先遍历左子树,每次遍历的时候,将我们需要往回遍历的节点放入栈中(往回遍历的节点就是拥有右子树的系点),在遍历完左节点后,此时回退上一个节点,看他的右子树是否为空。

且我们的整个循环的条件中 栈是否为空 就决定是否继续遍历这一个二叉树。

首先前序遍历的顺序 是根  左子树 右子树 。其次,对于二叉树,解决了整个左子树的遍历。那么整个右子树的遍历,也是与之相同的,也就是我们再解决问题时就只针对一个子树,这里我们就以遍历整个左子树为主,实现对整个树的遍历。且题目要求返回数组,因此我们是遍历插入数据。

由于我们无法知道某个左节点的右节点是否为空,所以开始我们先将所有左节点入栈:

第一步操作:

首先要遍历根,也就是从1开始,不为空,放入数组,并入栈,在看它的左,2不为空,放入数组,并入栈,再看它的左,3,不为空,放入数组并入栈。我们就完成了第一步,但是对于最后一个最左节点(它可能是一个子树的根,他也有可能是子树的左节点)。

第二步操作:

从这里开始,我们就要开始判断是否出栈,此时获取当前栈顶元素,3节点,再出栈。(此时栈不为空)

   若3节点无右节点,此时根已经遍历完了,现在轮到左子树了,而3此时是作为一个子树的左节点,并且是遍历顺序左子树中的第一个节点,之后出栈,看节点2的右子树是否存在。

   若3节点有右节点(右子树),比如我们这里就是节点4,3节点就往右子树走,按照前序遍历顺序,我们该将此节点数据放入数组中,因此以该节点为开始,继续第一步操作,将该右节点(也有可能是右子树),按照先把左节点入栈,入数组,在判断右子树情况来出栈。

第三步操作:

其实我们的前序遍历已经实现,可能有人还觉得还没完成,因为右子树还没处理,实际上当我们回退二叉树,也就是出栈的时候,出到最后一个元素,也就是真正的根节点时,栈不为空,循环还没结束,会继续判断当前节点的右子树。

代码实现:

vector<int> preorderTraversal(TreeNode* root) {
    
    //用来返回遍历结果的数组
    vector<int> v;
    
    //用栈来回退我们二叉树
    stack<TreeNode*> t;
    
    TreeNode*node=root;
    //当节点为空或者栈不为空的时候循环继续
    while(node||!t.empty())
    {
       //第一步将左边的节点一次放入数组并入栈 
        while(node)
        {
            v.push_back(node->val);
            t.push(node);
            node=node->left;
        }
        //从该位置开始判断是否要出栈
        node=t.top();
        t.pop();
        if(node->right==nullptr)
         {
            node=nullptr;//直接为空,继续循环进入到出栈操作
         }else
         {
           node=node->right;
         }      
    }
     return v;
    }

中序遍历

中序遍历 的顺序是 左节点 根 右子树 。与前序的插入思想不太一样,不过还是利用栈来回到上一个节点。前序遍历时,其实是有点巧合,因为根节点与左节点连续,我们是直接将左节点一个个入栈后,直接放入数组,因为顺序是从根节点开始,且根节点下来就是左节点,因此插入顺序与我们的直接将左节点插入的顺序一致。

但对于中序和后序遍历,都是先从左节点开始的,因此,从根开始,我们是先将之后的左节点一个个入栈,这里就不能再放数组了,直到最左节点时,根据我们的的遍历顺序,下来时根节点,再来判断。具体分析如下:

还是以这张图为例:

第一步:

刚开始我们还是直接以根为开始,将它的一个个左节点入栈,此时栈中为3,2,1。

第二步:

从这里开始,就需要判断是否出栈,是否插入数组,因此从这里开始,还是先获取栈顶元素,在出栈,当前位置就是节点3,之后就是判断3节点是否具有右子树。

若没有右子树,那么3就是第一个左节点,放入数组,之后回退到上一个节点2,继续判断。

若有右子树,此时节点3是一个根,不能放入数组,我们继续走到节点4,以4节点为开始,与前序遍历一样,执行第一步的操作进行入栈,之后在判断。

直到回退到根节点,此时进行右子树的遍历。

代码如下:

  vector<int> inorderTraversal(TreeNode* root) {
    vector<int> v;
    stack<TreeNode*> t;
    TreeNode* prev=nullptr;
    while(root||!t.empty())
    {
        while(root)
        {
         if(root)
         {
            t.push(root);
         }
         root=root->left;
        }
        root=t.top();
        t.pop();      

        //为空先插左节点
        v.push_back(root->val);
        if(root->right==nullptr)
        {
          root=nullptr;
        }else
        {
          root=root->right;
        }
            
    }
     return v;
    }

 后序遍历

后序遍历的思路与中序遍历有些许一样,但对于情况的判断更加复杂。

因为后序的遍历顺序为 左子树  右子树 根,育贤婿一样,我们还是从根节点开始,入栈左子树的所有左节点,直到最左节点,还是要进行判断。具体分析如下:

 还是一这张图为例:

第一步:

依然是将左子树的所有的左节点依次入栈, 1入,2入,3入,此时到了节点3。

第二步:

从节点3开始判断是否要回退,因此获取栈顶节点为3,出栈,当前节点为3。

若3没有右节点,此时我们就是左节点,我们就可以将3放入数组,之后回退到节点2。

若3的右节点存在,此时的操作是将该节点再次重新入栈(因为下一个右节点(右子树)插入完才轮到我),之后当前节点走到这个右节点,循环回到第一步,继续入栈。

在这里有重要的一点:

第一点:与之前两种遍历一样,我们需要从当前节点回退到上个节点的方法是将指针置空,之后重新赋值为栈顶元素的节点。

其次除了叶子节点容易判断插入,如果上一次插入的节点,是当前节点的右节点,则就需要放入数组里,即 先插的左, 之后插的右,根节点需要判断,当前节点的右节点是上一次插入的节点就说明是根,插入数组。

源码:

vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*>s;
        vector<int> ret;
        TreeNode*prev=nullptr;
     
        while(root||!s.empty())
        {
            //先找所有的左节点
            while(root)
            { 
                s.push(root);
                root=root->left;
            }
            //倒退判断这些节点的右节点,直到最后根节点,在判断右子树
            root=s.top(); 
            s.pop();
            if(root->right==nullptr ||root->right==prev) 
            {
                ret.push_back(root->val);
                prev=root;
                root=nullptr;

            }else
            {
                //若右子树不为空,继续入栈
                 s.push(root);
                root=root->right;
            }
        }
     return ret;
    }

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

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

相关文章

代码随想录二刷 |二叉树 |二叉树的层平均值

代码随想录二刷 &#xff5c;二叉树 &#xff5c;二叉树的层平均值 题目描述解题思路代码实现 题目描述 637.二叉树的层平均值 给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。 示例 1&#xff1a; 输…

Avaya Aura Device Services 任意文件上传漏洞复现

0x01 产品简介 Avaya Aura Device Services是美国Avaya公司的一个应用软件。提供一个管理 Avaya 端点功能。 0x02 漏洞概述 Avaya Aura Device Services 系统PhoneBackup接口处存在任意文件上传漏洞&#xff0c;攻击者可绕过验证上传任意文件获取服务器权限。 0x03 影响范围…

结构体和位段

结构体&#xff1a; C语言中&#xff0c;我们之前使用的都是C语言中内置的类型&#xff0c;比如整形&#xff08;int&#xff09;、字符型&#xff08;char&#xff09;、单精度浮点型&#xff08;float&#xff09;等。但是我们知道&#xff0c;我们现实世界中&#xff0c;还…

用Rust刷LeetCode之27 移除元素

27. 移除元素 难度: 简单 原描述: 新描述: func removeElement(nums []int, val int) int { for i : 0; i < len(nums); i { if nums[i] val { nums append(nums[:i], nums[i1:]...) i-- } } return len(nums)} Rust 版本 下面这种写法编译无法通过: pub fn remove_…

b样条原理与测试

为了保留贝塞尔曲线的优点&#xff0c;同时克服贝塞尔曲线的缺点&#xff0c;b样条在贝塞尔曲线上发展而来&#xff0c;首先来看贝塞尔曲线的定义&#xff1a; 对于贝塞尔中的基函数而言&#xff0c;是确定的&#xff0c;全局唯一的&#xff0c;这导致了如果控制点发生变换将会…

Linux基本指令(超详版)

Linux基本指令&#xff08;超详版&#xff09; 1. ls指令2.pwd指令3. cd 指令4.touch指令5mkdir指令6.rmdir指令&&rm指令7.man指令7.cp指令8.mv指令9.echo指令10.cat指令11.more指令12.less指令13.head指令14.tail指令15.date指令16.find指令17.grep指令zip(打包压缩) …

使用cmake构建Qt6.6的qt quick项目,添加应用程序图标的方法

最近&#xff0c;在学习qt的过程中&#xff0c;遇到了一个难题&#xff0c;不知道如何给应用程序添加图标&#xff0c;按照网上的方法也没有成功&#xff0c;后来终于自己摸索出了一个方法。 1、准备一张图片作为图标&#xff0c;保存到工程目录下面&#xff0c;如logo.ico。 …

二维码智慧门牌管理系统:引领未来的城市管理

文章目录 前言一、主要特点二、升级带来的优势与意义 前言 随着科技的快速发展&#xff0c;智能化管理已经成为我们生活和工作的重要方面。门牌管理系统是城市管理的基础设施之一&#xff0c;其智能化程度直接影响着城市管理的效率和质量。为了适应这一需求&#xff0c;二维码…

Helio 升级为 LISTA DAO,开启多链时代新篇章并宣布积分空投计划

Helio Protocol 是 BNB Chain 上排名第一的去中心化稳定币协议&#xff0c;其推出的超额抵押和清算机制支持的去中心化稳定币 HAY&#xff0c;在 BNB Chain 有非常广泛的应用&#xff0c;包括流动性挖掘、质押、交易、储值等&#xff01; 2023 年 7 月&#xff0c;Helio Protoc…

【小沐学Python】Python实现语音识别(SpeechRecognition)

文章目录 1、简介2、安装和测试2.1 安装python2.2 安装SpeechRecognition2.3 安装pyaudio2.4 安装pocketsphinx&#xff08;offline&#xff09;2.5 安装Vosk &#xff08;offline&#xff09;2.6 安装Whisper&#xff08;offline&#xff09; 3 测试3.1 命令3.2 fastapi3.3 go…

C#注册表技术及操作

目录 一、注册表基础 1.Registry和RegistryKey类 &#xff08;1&#xff09;Registry类 &#xff08;2&#xff09;RegistryKey类 二、在C#中操作注册表 1.读取注册表中的信息 &#xff08;1&#xff09;OpenSubKey()方法 &#xff08;2&#xff09;GetSubKeyNames()…

SpringSecurity6 | 自定义认证规则

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色专栏&#xf…

Java基础-java.util.Scanner接收用户输入

目录 1. 导入所需要的jar包2. 编写代码运行3. 输出运行结果 1. 导入所需要的jar包 import java.util.Scanner;2. 编写代码运行 public class ScannerDemo {public static void main(String[] args) {/** 使用Scanner接收用户键盘输入的数据* 1. 导包&#xff1a;告诉程序去JD…

角谷定理 C语言xdoj32

角谷定理定义如下&#xff1a; 对于一个大于1的整数n&#xff0c;如果n是偶数&#xff0c;则n n / 2。如果n是奇数&#xff0c;则n 3 * n 1&#xff0c;反复操作后&#xff0c;n一定为1。 例如输入22的变化过程&#xff1a; 22 ->11 -> 34 -> 17 -> 52 -> 26 …

探索 Python 中链表的实现:从基础到高级

# 更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 链表是一种基础的数据结构&#xff0c;它由一系列节点组成&#xff0c;每个节点都包含数据和指向下一个节点的引用。在Python中&#xff0c;可以使用类来实现链表&#xff0c;本文将介绍如何实现链表&#xff…

人工智能原理复习--搜索策略(二)

文章目录 上一篇启发式搜索与或图搜索博弈下一篇 上一篇 人工智能原理复习–搜索策略&#xff08;一&#xff09; 启发式搜索 提高一般图搜索效率的关键是优化OPEN表中节点的排序方式 最理想的情况是每次排序OPEN表表首n总在解答路径上 全局排序–对OPEN表中的所有节点进行…

论文阅读:PointCLIP: Point Cloud Understanding by CLIP

CVPR2022 链接&#xff1a;https://arxiv.org/pdf/2112.02413.pdf 0、Abstract 最近&#xff0c;通过对比视觉语言预训练(CLIP)的零镜头学习和少镜头学习在2D视觉识别方面表现出了鼓舞人心的表现&#xff0c;即学习在开放词汇设置下将图像与相应的文本匹配。然而&#xff0c;…

内外联动——记建行江门鹤山支行营业部堵截一起新型骗局

建设银行广东省江门市分行&#xff08;以下简称“江门建行”&#xff09;认真贯彻落实党中央、国务院决策部署&#xff0c;紧紧围绕当地市委工作部署和上级行要求&#xff0c;扛牢国有大行责任&#xff0c;坚守金融工作的政治性、人民性&#xff0c;以深化新金融行动助力江门全…

skynet 中 mongo 模块运作的底层原理解析

文章目录 前言总览全流程图涉及模块关系连接数据库函数调用流程图数据库操作函数调用流程图涉及到的代码文件 建立连接SCRAMSASL 操作数据库结语参考链接 前言 这篇文章总结 skynet 中 mongo 的接入流程&#xff0c;代码解析&#xff0c;读完它相信你对 skynet 中的 mongo 调用…

蓝桥杯日期问题

蓝桥杯其他真题点这里&#x1f448; 注意日期合法的判断 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;public class Main{static int[] days {0,31,28,31,30,31,30,31,31,30,31,30,31};static BufferedReader in new Buf…