当前位置: 首页 > news >正文

二叉树前序与后序遍历迭代法详解:栈操作与顺序反转的巧妙结合

二叉树的 前序遍历(根-左-右)和 后序遍历(左-右-根)是算法中的经典问题。虽然递归实现简单,但迭代法更能锻炼对栈操作的理解。本文将通过代码解析和出入栈模拟,彻底讲透这两种遍历的迭代实现,并揭示它们之间的巧妙联系。


一、前序遍历迭代法

1. 核心思路

前序遍历顺序:根节点 → 左子树 → 右子树。
迭代法核心:利用栈的 “先进后出” 特性,确保根节点先被访问,再处理左右子树。
关键操作

  • 根节点入栈 → 弹出根节点并记录 → 先右子节点入栈再左子节点入栈
  • 这样,左子树会先于右子树被处理。

2. 代码逐行解析

class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> res = new ArrayList<>();Stack<TreeNode> stack = new Stack<>();if (root != null) {stack.push(root); // 根节点入栈}while (!stack.isEmpty()) {TreeNode node = stack.pop(); // 弹出当前节点res.add(node.val);          // 记录根节点值// 右子节点先入栈(保证左子树先处理)if (node.right != null) {stack.push(node.right);}// 左子节点后入栈(后进先出)if (node.left != null) {stack.push(node.left);}}return res;}
}

3. 出入栈模拟(以二叉树 [1,2,3,4,5] 为例)

      1/ \2   3/ \4   5

前序遍历结果应为 [1,2,4,5,3]。栈的变化如下:

当前栈顶节点操作结果 res栈内容(底→顶)
1弹出 1,右3入栈,左2入栈[1][3,2]
2弹出 2,右5入栈,左4入栈[1,2][3,5,4]
4弹出 4,无子节点[1,2,4][3,5]
5弹出 5,无子节点[1,2,4,5][3]
3弹出 3,无子节点[1,2,4,5,3][]

二、后序遍历迭代法

1. 核心思路

后序遍历顺序:左子树 → 右子树 → 根节点。
迭代法核心

  • 逆向思维:前序遍历顺序为 根-左-右,若调整入栈顺序为 根-右-左,再将结果反转,即得到 左-右-根
  • 关键操作
    1. 根节点入栈 → 弹出根节点并记录 → 左子节点入栈 → 右子节点入栈。
    2. 最终反转结果列表。

2. 代码逐行解析

class Solution {public List<Integer> postorderTraversal(TreeNode root) {List<Integer> res = new ArrayList<>();Stack<TreeNode> stack = new Stack<>();if (root != null) {stack.push(root); // 根节点入栈}while (!stack.isEmpty()) {TreeNode node = stack.pop(); // 弹出当前节点res.add(node.val);          // 记录根节点值// 左子节点先入栈(保证右子树先处理)if (node.left != null) {stack.push(node.left);}// 右子节点后入栈(后进先出)if (node.right != null) {stack.push(node.right);}}Collections.reverse(res); // 反转结果return res;}
}

3. 出入栈模拟(以二叉树 [1,2,3,4,5] 为例)

后序遍历结果应为 [4,5,2,3,1]。栈的变化如下:

当前栈顶节点操作中间结果(未反转)栈内容(底→顶)
1弹出 1,左2入栈,右3入栈[1][2,3]
3弹出 3,无子节点[1,3][2]
2弹出 2,左4入栈,右5入栈[1,3,2][4,5]
5弹出 5,无子节点[1,3,2,5][4]
4弹出 4,无子节点[1,3,2,5,4][]
反转结果[4,5,2,3,1]

三、前序与后序遍历的关系

1. 核心规律

  • 前序遍历顺序:根 → 左 → 右
  • 调整后的前序顺序:根 → 右 → 左
  • 反转结果 → 后序遍历顺序:左 → 右 → 根

2. 为什么这样可行?

  • 调整入栈顺序后,实际遍历顺序为 根-右-左(例如 [1,3,2,5,4])。
  • 反转后得到 左-右-根(例如 [4,5,2,3,1]),即后序遍历结果。

四、关键知识点总结

1. 栈的作用

  • 模拟递归调用:显式栈替代系统栈,避免递归深度过大导致的栈溢出。
  • 控制访问顺序:通过入栈顺序调整遍历方向。

2. 时间复杂度与空间复杂度

  • 时间复杂度:O(n),每个节点恰好入栈和出栈一次。
  • 空间复杂度:O(n),最坏情况下栈存储所有节点。

3. 前序与后序的对比

遍历方式入栈顺序中间结果最终结果
前序遍历右 → 左根-左-右无需反转
后序遍历左 → 右根-右-左反转结果

五、完整代码

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

六、总结

  • 前序遍历:根节点先行,通过 “右左入栈” 保证顺序。
  • 后序遍历:调整前序入栈顺序为 “左右入栈”,反转结果即可。
  • 栈操作的核心:理解入栈顺序如何影响遍历方向,并利用反转技巧简化问题。
    掌握这一思路后,可以轻松应对其他二叉树遍历问题的迭代实现!
http://www.xdnf.cn/news/446581.html

相关文章:

  • NVMe简介1
  • Android 中 图片加载库 Glide 简介
  • 【Java-EE进阶】SpringBoot针对某个IP限流问题
  • Protocol Buffers 全流程通俗讲解
  • vLLM - SamplingParams 参数
  • 【BUG】滴答定时器的时间片轮询与延时冲突
  • 力扣热题——找出 3 位偶数
  • 康谋分享 | 自动驾驶仿真进入“标准时代”:aiSim全面对接ASAM OpenX
  • C++类和对象--高阶
  • 猫眼浏览器:简约安全,极速浏览
  • 基于多目标进化算法的神经网络架构搜索及其高级可视化技术
  • Huffman树
  • 常用的Java工具库
  • 错误: 加载主类 org.springframework.boot.loader.launch.JarLauncher 时出现 LinkageError
  • 鸿蒙Next API17新特性学习之如何使用新增鼠标轴事件
  • 蚂蚁seo强引蜘蛛池,SEO优化的利器
  • 【Linux笔记】——进程信号的捕捉——从中断聊聊OS是怎么“活起来”的
  • Kotlin Compose 与传统 Android UI 开发对比
  • LabVIEW在电子电工教学中的应用
  • Python 之 selenium 打开浏览器指定端口进行接续操作
  • Nginx+Lua 实战避坑:从模块加载失败到版本冲突的深度剖析
  • 数字信号处理-大实验1.1
  • Vue3吸顶导航的实现
  • Jmeter变量传递介绍
  • JavaScript 中级进阶技巧之map函数
  • 哈希表的实现01
  • java每日精进 5.14【参数校验】
  • qml中定时器的用法
  • 操作系统期末复习笔记
  • WHAT - 前端开发滚动场景API罗列