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

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

👉前言👈


二叉树的前中后遍历如果采取递归的方式来实现,是相当容易的事情。递归之所以强大,是因为有系统自动压栈。那么非递归的前中后序遍历就是借助栈,通过我们自己手动压栈来实现二叉树的遍历。当然除了递归和非递归的遍历方式,还有二叉树的 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


👉总结👈


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








相关文章
|
9天前
|
存储 算法 Java
算法系列之数据结构-二叉树
树是一种重要的非线性数据结构,广泛应用于各种算法和应用中。本文介绍了树的基本概念、常见类型(如二叉树、满二叉树、完全二叉树、平衡二叉树、B树等)及其在Java中的实现。通过递归方法实现了二叉树的前序、中序、后序和层次遍历,并展示了具体的代码示例和运行结果。掌握树结构有助于提高编程能力,优化算法设计。
42 9
 算法系列之数据结构-二叉树
|
15天前
|
存储 算法 Java
算法系列之递归反转单链表
递归反转链表的基本思路是将当前节点的next指针指向前一个节点,然后递归地对下一个节点进行同样的操作。递归的核心思想是将问题分解为更小的子问题,直到达到基本情况(通常是链表末尾)。
34 5
算法系列之递归反转单链表
|
2月前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
61 12
|
2月前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
60 10
|
2月前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
61 2
|
3月前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
2月前
|
数据采集 存储 算法
【C++数据结构——图】图的遍历(头歌教学实验平台习题) 【合集】
本文介绍了图的遍历算法,包括深度优先遍历(DFS)和广度优先遍历(BFS)。深度优先遍历通过递归方式从起始节点深入探索图,适用于寻找路径、拓扑排序等场景;广度优先遍历则按层次逐层访问节点,适合无权图最短路径和网络爬虫等应用。文中提供了C++代码示例,演示了如何实现这两种遍历方法,并附有测试用例及结果,帮助读者理解和实践图的遍历算法。
62 0
|
4月前
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
140 4
|
4月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
66 1
|
2月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
160 77