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

二叉树的遍历(前序、中序、后序)Java详解与代码实现

递归遍历

前序,中序,后序

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*///前序遍历
class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> res=new ArrayList<>();preorder(root,res);return res;}void preorder(TreeNode root, List<Integer> res){if(root==null){return;}res.add(root.val);preorder(root.left,res);preorder(root.right,res);}
}
//中序遍历
class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> res=new ArrayList<>();inorder(root,res);return res;}void inorder(TreeNode root,List<Integer> res){if(root==null){return;}inorder(root.left,res);res.add(root.val);inorder(root.right,res);}
}
//后序遍历
class Solution {public List<Integer> postorderTraversal(TreeNode root) {List<Integer> res=new ArrayList<>();postOrder(root,res);return res;}void postOrder(TreeNode root,List<Integer> res){if(root==null){return;}postOrder(root.left,res);postOrder(root.right,res);res.add(root.val);}
}

这三个遍历,理解起来都是差不多的

以前序遍历为例

以每一个树或子树的根节点和List集合作为函数的参数返回值类型是void.

如果碰到每一个树或子树的根节点是空,就结束递归,结束函数

否则,先把根节点的值收入集合,再把左右结点(子树)的值收入集合

最后调用函数之后,返回这个集合

迭代法(非递归)

前序,后序

前序

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> result=new ArrayList<>();if (root==null){return result;}Stack<TreeNode> stack=new Stack<>();stack.push(root);while (!stack.isEmpty()){TreeNode node=stack.pop();result.add(node.val);if (node.right!=null){stack.push(node.right);}if (node.left!=null){stack.push(node.left);}}return result;}
}

用栈模拟(实现)递归

先把树或某一子树的根节点入栈,然后,进入数组

再分别把右左子树的根节点入栈,然后分别将二者进入数组(右先入栈左后入栈,才能保证左先入数组,右后入数组)

这样左中右结点进入数组的顺序为:中左右

后序

以此类推,入栈的顺序为中左右,这样入数组的顺序为中右左,然后再将数组逆序对调

这样,数组输出的顺序就是左右中

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public List<Integer> postorderTraversal(TreeNode root) {List<Integer> result=new ArrayList<>();if (root==null){return result;}Stack<TreeNode> stack=new Stack<>();stack.push(root);while(!stack.isEmpty()){TreeNode node=stack.pop();result.add(node.val);if (node.left!=null){stack.push(node.left);}if (node.right!=null){stack.push(node.right);}}Collections.reverse(result);return result;}
}

要注意的是,Java中,List没有将集合元素逆序对调的方法,所以,要使用Collections的reverse方法

中序

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> res=new ArrayList<>();if (root==null){return res;}Stack<TreeNode> stack=new Stack<>();TreeNode cur=root;while(cur!=null||!stack.isEmpty()){if (cur!=null){stack.push(cur);cur=cur.left;}else{//就是,cur为空的时候cur=stack.pop();//出栈,并让cur指向他res.add(cur.val);//这里是处理中间结点cur=cur.right;//既然没有左子节点,那就看看有没有右子节点}}return res;}
}

这里的遍历方式和前序后序不一样了

前序后序差不多算是遍历到哪个结点就处理哪个节点。这里不一样,这里是先处理左节点,左子树的左子树的左子树的左……左节点……所以,要先遍历到最左的左节点,然后再看最左节点的根节点然后他的根结点的右子节点。

这里还用到了一个指针来指向要处理的树的结点

二叉树迭代遍历法的统一写法风格

我们以中序遍历为例,在二叉树:听说递归能做的,栈也能做! (opens new window)中提到说使用栈的话,无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况

那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。

如何标记呢,就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。 这种方法也可以叫做标记法

Java: 迭代法前序遍历代码如下:

class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> result = new LinkedList<>();Stack<TreeNode> st = new Stack<>();if (root != null) st.push(root);while (!st.empty()) {TreeNode node = st.peek();if (node != null) {st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)st.push(node);                          // 添加中节点st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。} else { // 只有遇到空节点的时候,才将下一个节点放进结果集st.pop();           // 将空节点弹出node = st.peek();    // 重新取出栈中元素st.pop();result.add(node.val); // 加入到结果集}}return result;}
}

迭代法中序遍历代码如下:

class Solution {
public List<Integer> inorderTraversal(TreeNode root) {List<Integer> result = new LinkedList<>();Stack<TreeNode> st = new Stack<>();if (root != null) st.push(root);while (!st.empty()) {TreeNode node = st.peek();if (node != null) {st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)st.push(node);                          // 添加中节点st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)} else { // 只有遇到空节点的时候,才将下一个节点放进结果集st.pop();           // 将空节点弹出node = st.peek();    // 重新取出栈中元素st.pop();result.add(node.val); // 加入到结果集}}return result;
}
}

迭代法后序遍历代码如下:

class Solution {public List<Integer> postorderTraversal(TreeNode root) {List<Integer> result = new LinkedList<>();Stack<TreeNode> st = new Stack<>();if (root != null) st.push(root);while (!st.empty()) {TreeNode node = st.peek();if (node != null) {st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中st.push(node);                          // 添加中节点st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)         } else { // 只有遇到空节点的时候,才将下一个节点放进结果集st.pop();           // 将空节点弹出node = st.peek();    // 重新取出栈中元素st.pop();result.add(node.val); // 加入到结果集}}return result;}
}

相关文章:

二叉树的遍历(前序、中序、后序)Java详解与代码实现

递归遍历 前序&#xff0c;中序&#xff0c;后序 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, Tree…...

如何找出消耗CPU最多的线程?

如何找出消耗CPU最多的线程&#xff1f; 1.使用 top -c 找出所有当前进程的运行列表 top -c 2.按P(Shiftp)对所有进程按CPU使用率进行排序&#xff0c;找出消耗最高的线程PID ​​​ 显示Java进程 PID 为 136 的java进程消耗最 3.使用 top -Hp PID&#xff0c;查出里面消…...

【论文笔记】Attention Augmented Convolutional Networks(ICCV 2019 入选文章)

目录 一、摘要 二、介绍 三、相关工作 卷积网络Convolutional networks&#xff1a; 网络中注意力机制Attention mechanisms in networks&#xff1a; 四、方法 1. 图像的自注意力Self-attention over images&#xff1a; 二维位置嵌入Two-dimensional Positional Enco…...

虚幻图文笔记:Character Creator 4角色通过AutoSetup For Unreal Engine插件导入UE5.1的过程笔记

在UE5端安装AutoSetup For Unreal Engine插件 AutoSetup For Unreal Engine是Reallusion官方提供的免费插件&#xff0c;官方下载地址&#xff0c;下载到的是一个可执行文件&#xff0c;点击安装&#xff0c;记住安装的位置⬇ 看装完毕后会打开一个文件夹&#xff0c;这里就是对…...

JAVAWeb04-DOM

1. DOM 1.1 概述 1.1.1 官方文档 地址: https://www.w3school.com.cn/js/js_htmldom.asp 1.1.2 DOM 介绍 DOM 全称是 Document Object Model 文档对象模型就是把文档中的标签&#xff0c;属性&#xff0c;文本&#xff0c;转换成为对象来管理 1.2 HTML DOM&#xff08;文档…...

C++内存管理基础知识

C 内存管理 C内存管理是一个重要的主题&#xff0c;因为它涉及到程序运行时资源的分配和释放。它可以分为三种类型&#xff1a;静态内存、栈内存和堆内存。 静态内存 静态内存&#xff08;Static Memory&#xff09;&#xff1a;静态内存用于存储全局变量、静态变量和常量。这…...

命令执行漏洞概述

命令执行漏洞概述 命令执行定义命令执行条件命令执行成因命令执行漏洞带来的危害远程命令执行漏洞相关函数assert()preg_replace()call_user_func() a ( a( a(b)可变函数远程命令执行漏洞的利用系统命令执行漏洞相关函数system()exec()shell_exec()passthru&#xff08;&#x…...

【初试复试第一】脱产在家二战上岸——上交819考研经验

笔者来自通信考研小马哥23上交819全程班学员 先介绍一下自己&#xff0c;我今年初试426并列第一&#xff0c;加上复试之后总分600&#xff0c;电子系第一。 我本科上交&#xff0c;本科期间虽然没有挂科但是成绩排名处于中下游水平。参加过全国电子设计大赛&#xff0c;虽然拿…...

PTA:C课程设计(7)

山东大学&#xff08;威海&#xff09;2022级大一下C习题集&#xff08;7&#xff09; 函数题7-6-1 递增的整数序列链表的插入7-6-2 查找学生链表7-6-3 统计专业人数7-6-4 建立学生信息链表 编程题7-7-1 查找书籍7-7-2 找出总分最高的学生 函数题 7-6-1 递增的整数序列链表的插…...

POSTGRESQL LINUX 与 PG有关的内存参释义

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…...

Docker的常见命令

前言:使用Docker得学会的几个常见命令 常见命令前置学习: docker --help这个命令必须得会因为,很多命令是记不住的,得使用他们的官方help下面是一些实例 docker load --help常见命令集合: 一: docker images #查看全部镜像 docker rmi #删除某个镜像(例如:docker rmi redis…...

详细介绍性能测试的方法(含文档)

性能测试是软件测试中的一个重要环节&#xff0c;其目的是评估系统在不同负荷下的性能表现&#xff0c;包括响应时间、吞吐量、并发数等指标。通常可以通过以下几种方法进行性能测试&#xff1a; 1、负载测试 负载测试是模拟多用户同时访问系统&#xff0c;测试系统在高并发、…...

深入剖析 Qt QHash :原理、应用与技巧

目录标题 引言QHash 基础用法基础用法示例基础用法综合示例 QHash 的高级用法迭代器&#xff1a;遍历 QHash 中的元素&#xff08;Iterators: Traversing Elements in QHash &#xff09;QHash和其他容器的对比QHash 和 std::unordered\_map QHash的底层原理和内存管理QHash 的…...

技术分享 | MySQL级联复制下进行大表的字段扩容

作者&#xff1a;雷文霆 爱可生华东交付服务部 DBA 成员&#xff0c;主要负责Mysql故障处理及相关技术支持。爱好看书&#xff0c;电影。座右铭&#xff0c;每一个不曾起舞的日子&#xff0c;都是对生命的辜负。 本文来源&#xff1a;原创投稿 *爱可生开源社区出品&#xff0c;…...

工业互联网业务知识

文章目录 背景第四次工业革命带动制造业产业升级主要工业大国不同路径 架构ISA95体系架构变革趋势基础通用架构数据采集平台 工业互联网应用软件工业互联网全要素连接产品视角&#xff1a;产销服务企业的业务流程企业数字化改造&#xff1a;车间级全要素连接 工业互联网的产品体…...

jsp+java自行车租赁租借和买卖系统

自行车租借和买卖系统 系统包括四个模块。1&#xff0c;系统模块&#xff0c;2&#xff0c;车辆管理模块&#xff0c;3.租借车管理模块&#xff0c;4&#xff0c;买卖车管理模块。 1&#xff0c;系统模块包括: 连接数据库&#xff0c;工作人员登录&#xff0c;退出。 2&#…...

Python3 字符串

Python3 字符串 字符串是 Python 中最常用的数据类型。我们可以使用引号( 或 " )来创建字符串。 创建字符串很简单&#xff0c;只要为变量分配一个值即可。例如&#xff1a; var1 Hello World! var2 "Runoob" Python 访问字符串中的值 Python 不支持单字符…...

Day943.持续集成流水线 -系统重构实战

持续集成流水线 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于持续集成流水线的内容。 从团队协作的角度上来看&#xff0c;在版本发布过程中&#xff0c;经常出现测试依赖开发手工生成制品、版本发布也从开发本地出版本的问题。而且项目架构如果从单体演进至组件…...

How to use CCS to debug a running M4F core that was started by Linux?

参考FAQ:AM62x & AM64x: How to use CCS to debug a running M4F core that was started by Linux? 问题记录&#xff1a; 1.使用SD卡启动模式&#xff0c;板上运行Linux。 当Linux系统启动后&#xff0c;9表示M4F core&#xff1a; am64xx-evm login: root rootam64xx…...

216、组合总数III

难度&#xff1a;中等 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。 示例 1: 输入: k 3, n 7…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…...

Xela矩阵三轴触觉传感器的工作原理解析与应用场景

Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知&#xff0c;帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量&#xff0c;能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度&#xff0c;还为机器人、医疗设备和制造业的智…...

TCP/IP 网络编程 | 服务端 客户端的封装

设计模式 文章目录 设计模式一、socket.h 接口&#xff08;interface&#xff09;二、socket.cpp 实现&#xff08;implementation&#xff09;三、server.cpp 使用封装&#xff08;main 函数&#xff09;四、client.cpp 使用封装&#xff08;main 函数&#xff09;五、退出方法…...

leetcode73-矩阵置零

leetcode 73 思路 记录 0 元素的位置&#xff1a;遍历整个矩阵&#xff0c;找出所有值为 0 的元素&#xff0c;并将它们的坐标记录在数组zeroPosition中置零操作&#xff1a;遍历记录的所有 0 元素位置&#xff0c;将每个位置对应的行和列的所有元素置为 0 具体步骤 初始化…...