【数据结构与算法】二叉树的非递归前中后序遍历

简介: 【数据结构与算法】二叉树的非递归前中后序遍历

👉前言👈


二叉树的前中后遍历如果采取递归的方式来实现,是相当容易的事情。递归之所以强大,是因为有系统自动压栈。那么非递归的前中后序遍历就是借助栈,通过我们自己手动压栈来实现二叉树的遍历。当然除了递归和非递归的遍历方式,还有二叉树的 Morris 遍历,这部分内容也将会在下一篇博客中呈现给大家!那话不多说,直接开整!


👉二叉树的前序遍历👈


给你二叉树的根节点 root ,返回它节点值的前序遍历。

033195f237ee4ba488970c4706abccd7.png

二叉树的非递归前序遍历实现步骤:


首先申请一个栈,如果头节点不为空,将头节点放入栈中。

当栈不为空时,while循环继续以下操作:弹出栈顶节点记为cur,然后对cur进行处理(在本道题中,处理cur的操作是将cur->val插入到vector的尾部)。如果节点cur的左右孩子均不为空,先将右孩子压入栈中,再将左孩子压入栈中。注意:如果孩子为空,就不需要压入栈中。

回到第 2 步,继续进行while循环判断。循环结束后,vector中存储的就是二叉树的前序遍历。


为了验证以上步骤的正确性,我为大家举了一个例子。见下图所示:


cb9df1fff71e4f50bc85772ef23be7f3.png


为什么以上过程就能够得到正确的前序遍历呢?因为入栈顺序是头、右、左,那么出栈顺序就是头、左、右(中序遍历的顺序)。注:出栈的顺序并不是左、右、头,因为头节点入栈后就出栈了,而左孩子出栈后,左孩子的左孩子和右孩子也要进栈,那么右孩子就被压在栈底了,并不是马上就能访问到它。


class Solution 
{
public:
    vector<int> preorderTraversal(TreeNode* root) 
    {
        vector<int> ret;
        if(root != nullptr)
        {
            // 头节点不为空,头节点先入栈
            stack<TreeNode*> st;
            st.push(root);
            // 栈不为空,while循环继续
            while(!st.empty())
            {
                // 弹出栈顶节点
                TreeNode* cur = st.top();
                st.pop();
                // 处理cur
                ret.push_back(cur->val);
                // 右孩子先入栈,左孩子后入栈
                if(cur->right != nullptr)
                {
                    st.push(cur->right);
                }
                if(cur->left != nullptr)
                {
                    st.push(cur->left);
                }
            }
        }
        return ret;
    }
};

789bd25298cd4ba2ae03cb2bdd61de7f.png

👉二叉树的后序遍历👈


给定一个二叉树的根节点 root ,返回 它的后序遍历 。

656c37259de840eeaecc14303c0370ba.png

二叉树的非递归后序遍历实现步骤:


首先申请两个栈,一个为辅助栈st1,另一个为收集栈st2。如果头节点不为空,将头节点压入辅助栈st1中。

当辅助栈st1不为空时,while循环继续以下操作:弹出栈顶节点记为cur,然后将cur压入收集栈st2中。如果节点cur的左右孩子均不为空,先将左孩子压入辅助栈st1中,再将右孩子压入辅助栈st1中。注意:如果孩子为空,就不需要压入栈中。

回到第 2 步,继续while循环判断。循环结束后,再将收集栈st2中的节点依次弹出就能够得到后序遍历的结果了。


为何以上步骤就能够得到正确的后序遍历结果呢?因为入辅助栈的顺序是头、左、右,那么出辅助栈的顺序是头、右、左(理由同前序遍历)。而出辅助栈的顺序就是入收集栈的顺序,即如收集栈的顺序就是头、右、左,所以出收集栈的顺序就是左、有、头(后序遍历的顺序)。注:收集栈是将全部节点收集完才依次出栈的,并不像辅助栈那样边出栈边入栈。如果还是不了解以上过程的,大家可以按照以上过程画一遍图,那么就会对以上过程会有更深的理解了。


class Solution 
{
public:
    vector<int> postorderTraversal(TreeNode* root) 
    {
        vector<int> ret;
        if(root != nullptr)
        {
            stack<TreeNode*> st1;   // 辅助栈
            stack<TreeNode*> st2;   // 收集栈
            // 头节点先入辅助栈
            st1.push(root);
            while(!st1.empty())
            {
                TreeNode* cur = st1.top();
                st1.pop();
                // cur压入收集栈中
                st2.push(cur);
                // 先将左孩子压入辅助栈,再将右孩子压入辅助栈
                if(cur->left != nullptr)
                {
                    st1.push(cur->left);
                }
                if(cur->right != nullptr)
                {
                    st1.push(cur->right);
                }
            }
            // 收集结果
            while(!st2.empty())
            {
                ret.push_back(st2.top()->val);
                st2.pop();
            }
        }
        return ret;
    }
};

bfb2bc4a43fb4ea8bdaa37660c1acf45.png


👉二叉树的中序遍历👈


给定一个二叉树的根节点 root ,返回 它的中序遍历 。

ee6117ca644a4afbbfe2019b08e83520.png

二叉树的非递归后序遍历实现步骤:


如果头节点不为空,则申请一个栈和一个指针变量TreeNode* cur并且头节点的左边界上的节点依次进栈。

当栈不为空或者cur不为空时,while循环继续一下操作:弹出栈顶节点赋值给cur,然后对cur进行处理(在本道题中,处理cur的操作是将cur->val插入到vector的尾部)。如果cur有右子树,则将右子树左边界上的节点依次压入栈中。

回到第 2 步,继续while循环判断。循环结束后,就能得到中序遍历的结果。


d408f09b66a04b73be6edf720f1e1f03.png


为什么按照以上步骤就能够得到正确的中序遍历呢?因为每一课二叉树都可以被左边界分解掉。入栈的顺序是头、左,出栈的顺序是左、头,且让左孩子的右子树的左边界入栈。那么出栈的顺序就是左、头、右了,也就是中序遍历的顺序。


class Solution 
{
public:
    vector<int> inorderTraversal(TreeNode* root) 
    {
        vector<int> ret;
        if(root != nullptr)
        {
            stack<TreeNode*> st;
            TreeNode* cur = root;
            while(!st.empty() || cur != nullptr)
            {
                // 左边界入栈
                if(cur != nullptr)
                {
                    st.push(cur);
                    cur = cur->left;
                }
                else    // 到达左边界的空节点了
                {
                    // 弹出栈顶节点
                    cur = st.top();
                    st.pop();
                    ret.push_back(cur->val);
                    // 如果右子树不为空,则下一次循环右子树的左边界会进栈
                    cur = cur->right;   
                }
            }
        }
        return ret;
    }
};

48da27410b9d40448d349dcc3a343edd.png


👉总结👈


本篇博客主要讲解了二叉树的非递归式前中后序遍历。那么以上就是本篇博客的全部内容了,如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家!💖💝❣️








相关文章
|
1天前
|
存储 缓存 算法
如何提高二叉树遍历算法的效率?
选择合适的遍历算法,如按层次遍历树时使用广度优先搜索(BFS),中序遍历二叉搜索树以获得有序序列。优化数据结构,如使用线索二叉树减少空指针判断,自定义节点类增加辅助信息。利用递归与非递归的特点,避免栈溢出问题。多线程并行遍历提高速度,注意线程安全。缓存中间结果,避免重复计算。预先计算并存储信息,提高遍历效率。综合运用这些方法,提高二叉树遍历算法的效率。
14 5
|
4天前
|
机器学习/深度学习 JSON 算法
二叉树遍历算法的应用场景有哪些?
【10月更文挑战第29天】二叉树遍历算法作为一种基础而重要的算法,在许多领域都有着不可或缺的应用,它为解决各种复杂的问题提供了有效的手段和思路。随着计算机科学的不断发展,二叉树遍历算法也在不断地被优化和扩展,以适应新的应用场景和需求。
10 0
|
28天前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
17 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
28天前
|
存储 算法 搜索推荐
数据结构与算法学习十七:顺序储存二叉树、线索化二叉树
这篇文章主要介绍了顺序存储二叉树和线索化二叉树的概念、特点、实现方式以及应用场景。
17 0
数据结构与算法学习十七:顺序储存二叉树、线索化二叉树
|
1月前
|
存储 算法
【二叉树】—— 算法题
【二叉树】—— 算法题
【二叉树】—— 算法题
|
28天前
|
存储 算法
数据结构与算法学习十六:树的知识、二叉树、二叉树的遍历(前序、中序、后序、层次)、二叉树的查找(前序、中序、后序、层次)、二叉树的删除
这篇文章主要介绍了树和二叉树的基础知识,包括树的存储方式、二叉树的定义、遍历方法(前序、中序、后序、层次遍历),以及二叉树的查找和删除操作。
22 0
|
25天前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
62 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
29天前
|
机器学习/深度学习 存储 缓存
数据结构与算法学习十:排序算法介绍、时间频度、时间复杂度、常用时间复杂度介绍
文章主要介绍了排序算法的分类、时间复杂度的概念和计算方法,以及常见的时间复杂度级别,并简单提及了空间复杂度。
20 1
数据结构与算法学习十:排序算法介绍、时间频度、时间复杂度、常用时间复杂度介绍
|
29天前
|
搜索推荐 算法
数据结构与算法学习十四:常用排序算法总结和对比
关于常用排序算法的总结和对比,包括稳定性、内排序、外排序、时间复杂度和空间复杂度等术语的解释。
17 0
数据结构与算法学习十四:常用排序算法总结和对比
|
29天前
|
存储 缓存 分布式计算
数据结构与算法学习一:学习前的准备,数据结构的分类,数据结构与算法的关系,实际编程中遇到的问题,几个经典算法问题
这篇文章是关于数据结构与算法的学习指南,涵盖了数据结构的分类、数据结构与算法的关系、实际编程中遇到的问题以及几个经典的算法面试题。
27 0
数据结构与算法学习一:学习前的准备,数据结构的分类,数据结构与算法的关系,实际编程中遇到的问题,几个经典算法问题