“非递归” 实现二叉树的“前序、中序、后序、层序”遍历

文章介绍了如何使用非递归方式实现二叉树的前序、后序、中序和层序遍历,主要利用栈和队列的数据结构。前序遍历和后序遍历通过调整压栈顺序实现,中序遍历则使用一个指针遍历并结合栈,而层序遍历则采用广度优先搜索(BFS)策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

前言

一、前序遍历

二、后续遍历

三、中序遍历

四、层序遍历


前言


        相信学过或是了解过二叉树的朋友都知道,他的前序、中序、后序遍历使用递归法实现非常简单,那么如果使用非递归的方式来解,也就是使用迭代来解,你还能将他拿下么?实际上一点都不难,往下看~

一、前序遍历


前序遍历,就是按照 根结点->左节点->右节点 的顺序去访问,如果要使用非递归的方式去访问,我们就离不开一个重要的数据结构——“栈”(非常多适合用递归做的题用非递归的方式去做都是这样的思路),下面我们就来看看如何具体操作!

假设我们有二叉树的根节点 root,那么首先我们可以创建一个栈,将这个根节点放入栈中,接着,只要栈不为空,我们就进行循环,循环什么呢?每次 pop 出一个结点,只要这个结点右结点不为空就push压栈,接着左节点不为空就push压栈,这样循环,最后得到的就是线序遍历的结果。

Ps:因为是先序遍历,所以先从栈中弹出一个结点,接着压栈顺序是前右结点后左节点(原理:栈是先进后出的)。

力扣链接:144. 二叉树的前序遍历

根据这个链接,检验以下你是否能做对吧!

 代码如下:

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList();
        if(root == null) {
            return list;
        }
        Stack<TreeNode> stack = new Stack();
        stack.push(root);
        while(!stack.isEmpty()) {
            TreeNode node = stack.pop();
            list.add(node.val);
            if(node.right != null) {
                stack.push(node.right);   
            }
            if(node.left != null){
                stack.push(node.left);
            }
        }
        return list;
    }
}

二、后续遍历


后序遍历实际上只需要在前序遍历的代码的基础上稍作修改即可~ 原本前序遍历的遍历顺序为"根 -> 左 -> 右",那么我们可以这样变一下,更改入栈的顺序,改成"根 -> 右 -> 左",也就是如下这几行代码进行一个转换

接着在对得到的数组进行反转,那么遍历顺序就变成了 "左 -> 右 -> 根",这正好就是我们要的后序遍历!

力扣链接:145. 二叉树的后序遍历

根据这个链接,检验以下你是否能做对吧!

代码如下:

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root == null) {
            return list;
        }
        Stack<TreeNode> stack = new Stack();
        stack.push(root);
        while(!stack.isEmpty()) {
            TreeNode node = stack.pop();
            list.add(node.val);
            //这里和前序遍历调换顺序
            if(node.left != null) {
                stack.push(node.left);
            }
            if(node.right != null) {
                stack.push(node.right);
            }
        }
        Collections.reverse(list);
        return list;
    }
}

三、中序遍历


中序遍历的迭代法思路就和 前序后序 不一样了,因为中序遍历的遍历顺序与读取结点的 val 值的顺序不一样~ 实际上,我们可以用 栈 来记录结点,用一个 cur指针 来遍历!

具体的,我们可以使用一个 cur指针 先指向根节点,然后进行循环,循环什么呢?当 cur指针 不为空时,就让 cur指针 一值向左走,边走边用栈记录下结点值,当 cur指针 为空时,我们就可以从栈中弹出元素给 cur指针 ,接着让 cur 指针向右结点走,接着循环以上步骤,直到 cur == null 并且 栈为空为止。

力扣链接:94. 二叉树的中序遍历

根据这个链接,检验以下你是否能做对吧!

代码如下:

    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root == null) {
            return list;
        }
        Stack<TreeNode> stack = new Stack();
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()) {
            if(cur != null) {
                //让 cur 一路向左走,如果当前遍历元素不为空就入栈
                stack.push(cur);
                cur = cur.left;
            } else {
                //若当前元素为空,说明左边走到头了,这时候就可以弹出元素
                cur = stack.pop();
                list.add(cur.val);
                cur = cur.right;
            }
        }
        return list;
    }

四、层序遍历


这里便是经典的 BFS,层序遍历便是从上到下获取到每一层的数据,再将每一层依次从左到右打印。具体的,我们可以使用一个队列,首先将根结点入栈,然后用一个 size 记录下当前行的有多少个结点,通过这个 size 我们便可以直到需要弹出这一行多少元素,每弹出一个元素,就检查他的左右孩子是否为空(层序遍历,先左后右,顺序不可以颠倒),不为空就继续放入栈中

力扣链接:94. 二叉树的中序遍历

根据这个链接,检验以下你是否能做对吧!

代码如下:

    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> ret = new ArrayList<>();
        if(root == null){
            return ret;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int size = queue.size();
            //row为一行的数据
            List<Integer> row = new ArrayList<>();
            //确定一层的数据
            while(size > 0){
                TreeNode cur = queue.poll();
                row.add(cur.val);
                if(cur.left != null){
                    queue.offer(cur.left);
                }
                if(cur.right != null){
                    queue.offer(cur.right);
                }
                size--;
            }
            //添加一层的数据
            ret.add(row);
        }
        return ret;
    }

<think>好的,我现在需要帮助用户理解二叉树前序、中后序遍历的概念、实现以及它们的区别。首,我需要回顾一下这三种遍历的基本定义,然后考虑它们的递归非递归实现方法,最后比较它们之间的区别和应用场景。 根据用户提供的引用内容,前序遍历的顺是根节点、左子树、右子树(DLR),中序遍历是左子树、根节点、右子树(LDR),后序遍历是左子树、右子树、根节点(LRD)。引用[1]中提到,前序列的第一个元素是整个树的根节点,通过在中列中找到根节点的位置来划分左右子树,这在构建二叉树时非常有用。引用[2]讨论了递归非递归实现,特别是非递归方法中如何处理左路节点和右子树,以及访问根节点的时机同。引用[3]则解释了遍历的命名规则和基本结构。 接下来,我需要将这些信息整理成一个清晰的回答结构。首解释每种遍历的概念和访问顺,然后分别说明递归非递归实现方式,可能还需要给出代码示例。最后比较它们的区别,比如访问根节点的顺同,应用场景的同(比如前序用于复制树,中用于二叉搜索树的有输出等)。 需要注意用户提到的系统级指令,特别是数学表达式要用$...$,但这里可能更多的是代码块的使用。需要确保代码示例正确,比如用Python或C++的语法。用户提供的引用中有C++的例子,但用户可能需要通用的解释,或者跨语言的示例。 另外,用户要求生成相关问题,我需要考虑与遍历相关的常见问题,比如如何根据遍历结果重建二叉树非递归实现的复杂度,遍历的应用等。 在组织内容时,要分点清晰,讲概念,再讲实现,最后比较区别。同时,引用文献需要自然地插入到相关内容后面,比如在提到前序遍历根节点时引用[1],在讨论非递归实现时引用[2]等。 现在需要检查是否有遗漏的部分,比如层序遍历是否要提及?但用户的问题主要围绕前序、中后序,所以可能需要。但引用[3]提到了层序遍历,可以作为扩展问题。 最后,确保语言简洁,用中文回答,避免使用Markdown格式,但代码块需要用指定的格式。同时,生成的相关问题要有针对性,帮助用户深入理解。</think>### 二叉树前序、中后序遍历详解 #### 一、概念与遍历 1. **前序遍历(DLR)** 访问顺:根节点 → 左子树 → 右子树 **示例**:若二叉树为 $A(B(D, E), C(F, \emptyset))$(其中 $\emptyset$ 表示空节点),前序遍历结果为 $A \ B \ D \ E \ C \ F$[^1]。 2. **中序遍历(LDR)** 访问顺:左子树 → 根节点 → 右子树 **示例**:上述二叉树的中序遍历结果为 $D \ B \ E \ A \ F \ C$[^3]。 3. **后序遍历(LRD)** 访问顺:左子树 → 右子树 → 根节点 **示例**:后序遍历结果为 $D \ E \ B \ F \ C \ A$。 --- #### 二、实现方法 ##### 1. 递归实现(以C++为例) ```cpp // 前序遍历 void preOrder(TreeNode* root) { if (root == nullptr) return; cout << root->val << " "; // 访问根 preOrder(root->left); // 左子树 preOrder(root->right); // 右子树 } // 中序遍历 void inOrder(TreeNode* root) { if (root == nullptr) return; inOrder(root->left); // 左子树 cout << root->val << " "; // 访问根 inOrder(root->right); // 右子树 } // 后序遍历 void postOrder(TreeNode* root) { if (root == nullptr) return; postOrder(root->left); // 左子树 postOrder(root->right); // 右子树 cout << root->val << " "; // 访问根 } ``` ##### 2. 非递归实现(核心思路) - **前序遍历**:使用栈保存右子树,优访问根节点后再处理左子树[^2]。 - **中序遍历**:将左路节点全部入栈,出栈时访问根节点,再处理右子树。 - **后序遍历**:需记录上一个访问的节点,确保右子树已处理完毕再访问根节点。 --- #### 三、关键区别 | 遍历方式 | 根节点访问时机 | 典型应用场景 | |----------|----------------|--------------| | 前序遍历 | 最访问根节点 | 复制二叉树结构、前缀表达式 | | 中序遍历 | 中间访问根节点 | 二叉搜索树有输出、中缀表达式 | | 后序遍历 | 最后访问根节点 | 释放内存资源、后缀表达式 | --- #### 四、相关问题 1. 如何通过前序和中序遍历列重建二叉树? 2. 非递归遍历的时间复杂度是多少?如何优化? 3. 为什么中序遍历对二叉搜索树特别重要? 4. 层序遍历与前三种遍历有何本质区别? : 前序列的第一个元素是根节点,用于划分左右子树结构。 [^2]: 非递归实现通过栈模拟递归过程,访问根的时机决定遍历类型。 : 三种遍历本质是深度优搜索,而层序遍历属于广度优搜索。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈亦康

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值