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

Java 二叉树非递归遍历核心实现

在这里插入图片描述

非递归遍历的核心是用栈模拟递归的调用过程,通过手动维护栈来替代系统栈,实现前序、中序和后序遍历。以下是三种遍历的代码实现与关键逻辑分析:


一、二叉树遍历

1.1、前序遍历(根 → 左 → 右)

核心逻辑:访问根节点后,先压右子节点再压左子节点(利用栈的 LIFO 特性)。
步骤

  1. 根节点入栈。
  2. 循环弹出栈顶元素并访问。
  3. 若存在右子节点,入栈;若存在左子节点,入栈。
public List<Integer> preorderTraversal(TreeNode root) {List<Integer> res = new ArrayList<>();if (root == null) return res;final LinkedList<TreeNode> stack = new LinkedList<>();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;
}

1.2、中序遍历(左 → 根 → 右)

核心逻辑:先遍历到最左叶子节点,再回溯处理中间节点和右子树。
步骤

  1. 持续压左子节点入栈,直到左子为空。
  2. 弹出栈顶元素访问,并将当前指针转向右子节点。
public List<Integer> inorderTraversal(TreeNode root) {List<Integer> res = new ArrayList<>();final LinkedList<TreeNode> stack = new LinkedList<>();TreeNode cur = root;while (cur != null || !stack.isEmpty()) {// 压左子树到栈底while (cur != null) {stack.push(cur);cur = cur.left;}cur = stack.pop();res.add(cur.val);     // 访问当前节点(左子处理完)cur = cur.right;      // 转向右子树}return res;
}

1.3、后序遍历(左 → 右 → 根)

核心逻辑:需确保左右子树均已处理后再访问根节点,常用双栈法标记法

方法1:双栈法(反向输出根右左)

public List<Integer> postorderTraversal(TreeNode root) {List<Integer> res = new ArrayList<>();if (root == null) return res;final LinkedList<TreeNode> stack1 = new LinkedList<>();final LinkedList<TreeNode> stack2 = new LinkedList<>();stack1.push(root);while (!stack1.isEmpty()) {TreeNode node = stack1.pop();stack2.push(node);        // 辅助栈存储逆序if (node.left != null) stack1.push(node.left);  // 左先入栈1if (node.right != null) stack1.push(node.right); // 右后入栈1}// 逆序输出即为后序while (!stack2.isEmpty()) {res.add(stack2.pop().val);}return res;
}

方法2:单栈标记法(记录访问状态)

public List<Integer> postorderTraversal(TreeNode root) {List<Integer> res = new ArrayList<>();final LinkedList<Pair<TreeNode, Boolean>> stack = new LinkedList<>();stack.push(new Pair<>(root, false));while (!stack.isEmpty()) {Pair<TreeNode, Boolean> pair = stack.pop();TreeNode node = pair.getKey();boolean visited = pair.getValue();if (node == null) continue;if (visited) {res.add(node.val);} else {stack.push(new Pair<>(node, true));   // 根标记为已访问stack.push(new Pair<>(node.right, false));stack.push(new Pair<>(node.left, false));/* 中序stack.push(new Pair<>(node.right, false));stack.push(new Pair<>(node, true));   // 根标记为已访问stack.push(new Pair<>(node.left, false));*//* 先序stack.push(new Pair<>(node.right, false));stack.push(new Pair<>(node.left, false));stack.push(new Pair<>(node, true));   // 根标记为已访问*/}}return res;
}class Pair<k,B>{public Pair(k key, B value) {this.key = key;this.value = value;}k key;B value;public k getKey() {return key;}public void setKey(k key) {this.key = key;}public B getValue() {return value;}public void setValue(B value) {this.value = value;}
}

二、关键对比与总结

遍历方式栈操作特点时间复杂度空间复杂度
前序根 → 右 → 左入栈,出栈顺序根 → 左 → 右O(n)O(n)
中序持续压左子,回溯时处理根和右子O(n)O(n)
后序双栈反转根右左为左右根,或标记访问状态O(n)O(n)

优化建议

  • 统一写法:通过标记法(如 Pair<节点, 是否已访问>)可统一三种遍历,仅调整入栈顺序。
  • 避免大栈深度:对于极不平衡的树(如链状结构),递归可能导致栈溢出,非递归更安全。

适用场景

  • 前序:快速复制树结构(先创建父节点)。
  • 中序:二叉搜索树的有序输出。
  • 后序:释放子树内存(先处理子节点再父节点)。

三、常见问题

  1. 为什么后序遍历比前序/中序复杂?
    后序需确保左右子树均处理完才能访问根节点,需额外机制(如辅助栈或标记状态)保证顺序。

  2. 非递归和递归的性能差异?
    递归代码简洁但隐含函数调用栈开销;非递归手动管理栈,空间复杂度相同,但常数因子更优。

  3. 如何处理层次遍历?
    使用队列(BFS),而非栈(DFS)。每次处理一层节点,按层加入结果列表。

相关文章:

Java 二叉树非递归遍历核心实现

非递归遍历的核心是用栈模拟递归的调用过程&#xff0c;通过手动维护栈来替代系统栈&#xff0c;实现前序、中序和后序遍历。以下是三种遍历的代码实现与关键逻辑分析&#xff1a; 一、二叉树遍历 1.1、前序遍历&#xff08;根 → 左 → 右&#xff09; 核心逻辑&#xff1a;…...

JavaScript性能优化实践:从微观加速到系统级策略

JavaScript性能优化实践:从微观加速到系统级策略 引言:性能优化的"时空折叠"思维 在JavaScript的世界里,性能优化如同在时间与空间的维度中折叠代码。本文将通过"时空折叠"的隐喻,从代码执行效率(时间维度)和内存占用(空间维度)两大核心,结合现代…...

《P1029 [NOIP 2001 普及组] 最大公约数和最小公倍数问题》

题目描述 输入两个正整数 x0​,y0​&#xff0c;求出满足下列条件的 P,Q 的个数&#xff1a; P,Q 是正整数。 要求 P,Q 以 x0​ 为最大公约数&#xff0c;以 y0​ 为最小公倍数。 试求&#xff1a;满足条件的所有可能的 P,Q 的个数。 输入格式 一行两个正整数 x0​,y0​。…...

【力扣hot100题】(052)课程表

什么人一学期要上2000节课啊jpg 看了非常久都没思路&#xff0c;主要是数据结构还没复习到图论&#xff0c;根本没思路怎么储存一个图…… 唯一记得的就是两种存储方法&#xff0c;一种是二维数组法&#xff0c;记录每一条边的有无&#xff0c;一种是只记录有的边&#xff0c…...

SpringBoot配置文件多环境开发

目录 一、设置临时属性的几种方法 1.启动jar包时&#xff0c;设置临时属性 ​2.idea配置临时属性 3.启动类中创建数组指定临时属性 二、多环境开发 1.包含模式 2.分组模式 三、配置文件的优先级 1.bootstrap 文件优先&#xff1a; 2.特定配置文件优先 3.文件夹位置优…...

RSA和ECC在密钥长度相同的情况下哪个更安全?

​现在常见的SSL证书&#xff0c;如&#xff1a;iTrustSSL都支持RSA和ECC的加密算法&#xff0c;正常情况下RAS和ECC算法该如何选择呢&#xff1f;实际上在密钥长度相同的情况下&#xff0c;ECC&#xff08;椭圆曲线密码学&#xff09;通常比RSA&#xff08;Rivest-Shamir-Adle…...

Dive into Deep Learning - 2.4. Calculus (微积分)

Dive into Deep Learning - 2.4. Calculus {微积分} 1. Derivatives and Differentiation (导数和微分)1.1. Visualization Utilities 2. Chain Rule (链式法则)3. DiscussionReferences 2.4. Calculus https://d2l.ai/chapter_preliminaries/calculus.html For a long time, …...

【备考高项】附录:合同法全文(428条全)

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 第一章 一般规定第二章 合同的订立第三章 合同的效力第四章 合同的履行第五章 合同的变更和转让第六章 合同的权利义务终止第七章 违约责任第八章 其他规定第九章 买卖合同第十章 供用电、水、气、热力合同第十…...

Ubuntu安装Podman教程

1、先修改apt源为阿里源加速 备份原文件&#xff1a; sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup 修改源配置&#xff1a; vim sources.list删除里面全部内容后&#xff0c;粘贴阿里源&#xff1a; deb http://mirrors.aliyun.com/ubuntu/ focal main re…...

9.进程信号

信号量 信号量是什么&#xff1f; ​ 本质是一个计数器&#xff0c;通常用来表示公共资源中&#xff0c;资源数量多少的问题。 ​ 公共资源&#xff1a;可以被多个进程同时访问的资源。 访问没有保护的公共资源会导致数据不一致问题 什么是数据不一致问题 ​ 由于公共资源…...

python爬虫:小程序逆向(需要的工具前期准备)

前置知识点 1. wxapkg文件 如何查看小程序包文件 打开wechat的设置&#xff1a; .wxapkg概述 .wxapkg是小程序的包文件格式&#xff0c;且其具有独特的结构和加密方式。它不仅包含了小程序的源代码&#xff0c;还包括了图像和其他资源文件&#xff0c;这些内容在普通的文件…...

PGSQL 对象创建函数生成工具

文章目录 代码结果 代码 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>PGSQL 函数生成器</tit…...

查询当前用户的购物车和清空购物车

业务需求&#xff1a; 在小程序用户端购物车页面能查到当前用户的所有菜品或者套餐 代码实现 controller层 GetMapping("/list")public Result<List<ShoppingCart>> list(){List<ShoppingCart> list shoppingCartService.shopShoppingCart();r…...

八、重学C++—动态多态(运行期)

上一章节&#xff1a; 七、重学C—静态多态&#xff08;编译期&#xff09;-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/146999362?spm1001.2014.3001.5502 本章节代码&#xff1a; cpp/dynamicPolymorphic.cpp CuiQingCheng/cppstudy - 码云 - 开源中…...

react redux的学习,多个reducer

redux系列文章目录 第一章 简单学习redux,单个reducer 前言 前面我们学习到的是单reducer的使用&#xff1b;要知道redux是个很强大的状态存储库&#xff0c;可以支持多个reducer的使用。 combineReducers ‌combineReducers‌是Redux中的一个辅助函数&#xff0c;主要用于…...

饮食助力进行性核上性麻痹患者,提升生活质量

进行性核上性麻痹是一种少见的神经系统变性疾病&#xff0c;患者会出现姿势不稳、眼球运动障碍等症状。合理的饮食对于维持患者身体机能、延缓病情发展有重要意义。 高蛋白质食物是饮食结构的重要部分。像瘦肉、去皮禽肉、鱼类、豆类及其制品&#xff0c;还有低脂奶制品等&…...

leetcode117 填充每个节点的下一个右侧节点指针2

LeetCode 116 和 117 都是关于填充二叉树节点的 next 指针的问题&#xff0c;但它们的区别在于 树的类型 不同&#xff0c;117与 116 题类似&#xff0c;但给定的树是 普通二叉树&#xff08;不一定完全填充&#xff09;&#xff0c;即某些节点可能缺少左或右子节点。 树的结构…...

bun 版本管理工具 bum 安装与使用

在使用 node 的过程中&#xff0c;我们可能会因为版本更新或者不同项目的要求而频繁切换 node 版本&#xff0c;或者是希望使用更简单的方式安装不同版本的 node&#xff0c;这个时候我们一般会用到 nvm 或者类似的工具。 在我尝试使用 bun 的时候&#xff0c;安装前第一个想到…...

使用RKNN进行yolo11-cls部署

文章目录 概要制作数据集模型训练onnx导出rknn导出概要 YOLO(You Only Look Once)是一系列高效的目标检测算法,其核心思想是将目标检测任务转化为一个回归问题,通过单个神经网络直接在图像上预测边界框和类别概率。当将其用于分类任务时,会去除目标检测相关的边界框预测部…...

木马学习记录

一句话木马是什么 一句话木马就是仅需要一行代码的木马&#xff0c;很简短且简单&#xff0c;木马的函数将会执行我们发送的命令 如何发送命令&#xff06;发送的命令如何执行? 有三种方式&#xff1a;GET&#xff0c;POST&#xff0c;COOKIE&#xff0c;一句话木马中用$_G…...

C#编程基础知识点介绍

以下是 C# 中常见元素&#xff08;属性、方法、枚举、函数等&#xff09;的详细定义及示例&#xff1a; 1. 类&#xff08;Class&#xff09; 类是 C# 中最基本的类型&#xff0c;它是对象的蓝图&#xff0c;封装了数据和行为。 // 定义一个名为Person的类 public class Per…...

决策树实战:用Python实现智能分类与预测

目录 一、环境准备 二、数据加载与探索 三、数据预处理 四、决策树模型构建 五、模型可视化&#xff08;生成决策树结构图&#xff09; 六、模型预测与评估 七、超参数调优&#xff08;网格搜索&#xff09; 八、关键知识点解析 九、完整项目开发流程 十、常见问题解…...

Crond任务调度

今天我们来看看任务调度,假如我们正在睡觉,突然有个半夜两点的任务要你备份一下数据库,你怎么办&#xff1f;难道从被窝中爬起来吗&#xff1f;显然不合理,此时就需要我们定时任务调度程序了. 原理图&#xff1a; crontab 进行定时任务的调度 概述. 任务调度:是指系统在某个…...

HTML5+CSS3+JS小实例:带滑动指示器的导航图标

实例:带滑动指示器的导航图标 技术栈:HTML+CSS+JS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, ini…...

MINIQMT学习课程Day7

在上一篇&#xff0c;我们安装好xtquant&#xff0c;qmt以及python后&#xff0c;这一章&#xff0c;我们学习如何使用xtquant 本章学习&#xff0c;如何获取账号的资金使用状况。 首先&#xff0c;打开qmt&#xff0c;输入账号密码&#xff0c;选择独立交易。 进入交易界面&…...

每日一个小病毒(C++)EnumChildWindows+shellcode

这里写目录标题 1. `EnumChildWindows` 的基本用法2. 如何利用 `EnumChildWindows` 执行 Shellcode?关键点:完整 Shellcode 执行示例3. 为什么 `EnumChildWindows` 能执行 Shellcode?4. 防御方法5. 总结EnumChildWindows 是 Windows API 中的一个函数,通常用于枚举所有子窗…...

使用minio客户端mc工具迁移指定文件到本地

如果需要筛选MinIO桶中的特定文件进行迁移&#xff0c;可以使用MinIO Client&#xff08;mc&#xff09;工具结合一些命令行技巧来实现。以下是具体步骤&#xff1a; 1、安装 MinIO Client&#xff08;mc&#xff09; wget https://dl.min.io/client/mc/release/linux-amd64/…...

git clone 提示需要登录 github

我们在进行git的时候&#xff0c;可能会弹出让你登陆github的选项&#xff0c;这里我们介绍Token登陆的方法。 正常登陆你的Github 下拉找到 Developer settings按照如下步骤进行操作 填写相关信息&#xff0c;勾选对应选项 返回就能看到token已经被生成&#xff0c;可以使…...

4.2-3 fiddler抓取手机接口

安卓&#xff1a; 长按手机连接的WiFi&#xff0c;点击修改网络 把代理改成手动&#xff0c;服务器主机选择自己电脑的IP地址&#xff0c;端口号为8888&#xff08;在dos窗口输入ipconfig查询IP地址&#xff0c;为ipv4&#xff09; 打开手机浏览器&#xff0c;输入http://自己…...

Nacos注册中心AP模式核心源码分析(单机模式)

文章目录 概述一、客户端启动主线流程源码分析1.1、客户端与Spring Boot整合1.2、注册实例&#xff08;服务注册&#xff09;1.3、发送心跳1.4、拉取服务端实例列表&#xff08;服务发现&#xff09; 二、服务端接收请求主线流程源码分析2.1、接收注册请求2.1.1、初始化注册表2…...