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

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

👉前言👈


二叉树的前中后遍历如果采取递归的方式来实现,是相当容易的事情。递归之所以强大,是因为有系统自动压栈。那么非递归的前中后序遍历就是借助栈,通过我们自己手动压栈来实现二叉树的遍历。当然除了递归和非递归的遍历方式,还有二叉树的 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月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
32 1
|
1月前
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
82 4
|
1月前
|
算法
分享一些提高二叉树遍历算法效率的代码示例
这只是简单的示例代码,实际应用中可能还需要根据具体需求进行更多的优化和处理。你可以根据自己的需求对代码进行修改和扩展。
|
1月前
|
存储 缓存 算法
如何提高二叉树遍历算法的效率?
选择合适的遍历算法,如按层次遍历树时使用广度优先搜索(BFS),中序遍历二叉搜索树以获得有序序列。优化数据结构,如使用线索二叉树减少空指针判断,自定义节点类增加辅助信息。利用递归与非递归的特点,避免栈溢出问题。多线程并行遍历提高速度,注意线程安全。缓存中间结果,避免重复计算。预先计算并存储信息,提高遍历效率。综合运用这些方法,提高二叉树遍历算法的效率。
58 5
|
1月前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
132 8
|
1月前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
50 2
|
1月前
|
机器学习/深度学习 JSON 算法
二叉树遍历算法的应用场景有哪些?
【10月更文挑战第29天】二叉树遍历算法作为一种基础而重要的算法,在许多领域都有着不可或缺的应用,它为解决各种复杂的问题提供了有效的手段和思路。随着计算机科学的不断发展,二叉树遍历算法也在不断地被优化和扩展,以适应新的应用场景和需求。
43 0
|
2月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
32 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
2月前
|
存储 算法 搜索推荐
数据结构与算法学习十七:顺序储存二叉树、线索化二叉树
这篇文章主要介绍了顺序存储二叉树和线索化二叉树的概念、特点、实现方式以及应用场景。
37 0
数据结构与算法学习十七:顺序储存二叉树、线索化二叉树
|
2月前
|
存储 算法
探索数据结构:分支的世界之二叉树与堆
探索数据结构:分支的世界之二叉树与堆