二叉树之遍历OJ(含迭代)

目录

1.递归实现

前言

(1)前序遍历

(2)中序遍历

(3)后序遍历

2.迭代实现

前言

(1)前序遍历

 方法一

方法二

(2)中序遍历

方法一

方法二 

(3)后序遍历

方法一

方法二

(4)层序遍历


1.递归实现

前言

此篇文章我们将详细理解二叉树的遍历,想必大家跟我一样第一次学习二叉树非常头疼,它是怎么递归的呀?怎么搞也搞不明白,想将递归的过程画出来却越画越乱,不画却又不理解,因此借此篇文章谈谈我是怎么理解二叉树中的递归的

在写递归时我们可以遵循以下四个步骤:

  1. 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。

  2. 将原问题化为子问题:类比循环我们每次执行的代码应该是相同的,但子问题是需要把计算结果返回上一级问题,也就是递归的每一层都要有返回值来给到上一层

  3. 确定非边界条件:确定非边界条件时我们不要一上来就陷入递归中的细节,我们可以将左子树看作一个整体,右子树看作一个整体来思考确定非边界条件

  4. 确定边界条件:我们通过子问题来确定递归的边界条件,由于子问题规模较小,当代码一层一层往里递的时候总会有一个终止的条件,即边界条件,此时就直接返回它的答案,即归的操作

(1)前序遍历

前序遍历的核心:前序位置的代码在刚刚进入一个二叉树节点的时候执行;

前序遍历OJicon-default.png?t=N7T8https://leetcode.cn/problems/binary-tree-preorder-traversal/
第一步确定递归函数的参数和返回值:

因为要打印前序遍历的节点值,因此第一个参数为 TreeNode root

每次打印之后我们都需要存储到List中,因此第二个参数为 List<Integer> result

返回void即可

public void preorder(TreeNode root,List<Integer> result)

第二步将原问题化为子问题

一棵二叉树的前序遍历结果 = 根节点 + 左子树的前序遍历结果 + 右子树的前序遍历结果。

第三步确定非边界条件:

我们想打印整棵树的节点值,即打印根节点+左子树+右子树的节点值即可

//根节点
result.add(root.val);
//左
preorder(root.left, result);
//右
preorder(root.right, result);

第四步确定边界条件:

当我们一直往里递时,总会有一个尽头,即节点为空时我们不再进行遍历打印,此时就开始归

if (root == null) {return;
}

整体代码:

class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<Integer>();preorder(root,result);return result;}public void preorder(TreeNode root,List<Integer> result) {//刚刚进入一个二叉树节点的时候执行;if (root == null) {return;}//根节点result.add(root.val);//左preorder(root.left, result);//右preorder(root.right, result);}
}

(2)中序遍历

中序遍历的核心:中序位置的代码在一个二叉树节点左子树都遍历完,即将开始遍历右子树的时候执行。

中序遍历OJicon-default.png?t=N7T8https://leetcode.cn/problems/binary-tree-inorder-traversal/description/

class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<Integer>();preorder(root,result);return result;}public void preorder(TreeNode root,List<Integer> result) {if (root == null) {return;}//左preorder(root.left, result);//根节点result.add(root.val);//右preorder(root.right, result);}
}

(3)后序遍历

后续遍历的核心:后序位置的代码在将要离开一个二叉树节点的时候执行;

后序遍历OJicon-default.png?t=N7T8https://leetcode.cn/problems/binary-tree-postorder-traversal/

class Solution {public List<Integer> postorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<Integer>();inorder(root,result);return result;}public void inorder(TreeNode root,List<Integer> result) {//后序位置的代码在将要离开一个二叉树节点的时候执行;if(root == null) {return;}//左inorder(root.left,result);//右inorder(root.right,result);//中result.add(root.val);}
}

2.迭代实现

前言

为什么可以用迭代法(非递归的方式)来实现二叉树的前后中序遍历呢?
递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。

(1)前序遍历

 方法一

充分利用栈的特点,前序遍历顺序:中-左-右,入栈顺序:中-右-左

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;}
}

方法二

根左右,刚进入节点就执行

思路:因为前序遍历的顺序为根左右,我们定义一个cur变量从根节点出发,沿着左子树依次入栈并打印,直到cur为空时停止,紧接着弹出当前节点(相当于递归中归的操作,回退到上一个节点进行判断)并记录判断当前节点是否有右子树
遍历左子树的循环条件为cur != null,只要cur不为空我们就一直循环入栈并打印的操作

遍历右子树的循环条件是什么呢?当我们走完遍历左子树的操作想要紧接着判断右子树,而且我们不知道右子树下面是否还存在一颗完整的树或左子树,因此我们要将两个循环写在一个大循环下

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

(2)中序遍历

方法一

中序遍历顺序: 左-中-右 入栈顺序: 左-右
// 中序遍历顺序: 左-中-右 入栈顺序: 左-右
class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();if (root == null){return result;}Stack<TreeNode> stack = new Stack<>();TreeNode cur = root;while (cur != null || !stack.isEmpty()){if (cur != null){stack.push(cur);cur = cur.left;}else{cur = stack.pop();result.add(cur.val);cur = cur.right;}}return result;}
}

方法二 

左根右,左子树都遍历完再执行 

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

(3)后序遍历

方法一

后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果

// 后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果
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;}
}

方法二

左右根   将要离开一个二叉树节点的时候执行;

该方法需要在打印根节点前,标记根节点,防止重复打印;

当我们走else语句到达H时,进行下一次循环,cur != null,又将H进行了入栈,cur = cur.left,此时cur为空跳出while循环判断右子树,top为H,H的右子树为空,弹出H并打印,此时cur为空,没进入while循环,而top此时为D,D的右树不为空,又走了else将H入栈并判断造成了死循环,所以我们这里需要进行标记,打印过该节点就不再进行打印,直接判断上一个节点

class Solution {public List<Integer> postorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<Integer>();inorder(root,result);return result;}public void inorder(TreeNode root,List<Integer> result) {Stack<TreeNode> stack = new Stack<>();TreeNode cur = root;TreeNode prev = null;while (cur != null || !stack.isEmpty()) {while (cur != null) {stack.push(cur);cur = cur.left;}TreeNode top = stack.peek();if (top.right == null || top.right == prev) {//打印当前top 并且弹出stack.pop();result.add(top.val);prev = top;} else {cur = top.right;}}}
}

(4)层序遍历

层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。

需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。


思路:先将根节点入队,在出队的同时将当前节点的左右节点入队(需要判断左右节点是否存在)循环这一操作即可
 

二叉树层序遍历OJicon-default.png?t=N7T8https://leetcode.cn/problems/binary-tree-level-order-traversal/description/

public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> ret = new ArrayList<List<Integer>>();if (root == null) {return ret; // 如果根节点为空,直接返回空的二维列表}Queue<TreeNode> queue = new LinkedList<TreeNode>();queue.offer(root); // 先将根节点放入队列while (!queue.isEmpty()) {List<Integer> list = new ArrayList<>();int size = queue.size(); // 记录当前层的节点数for (int i = 0; i < size; i++) {TreeNode cur = queue.poll(); // 出队// list.add(cur.val); // 将当前节点的值加入当前层的列表中// 将当前节点的子节点(如果有的话)加入队列if (cur.left != null) {queue.offer(cur.left);}if (cur.right != null) {queue.offer(cur.right);}}// 将当前层的列表加入结果列表中ret.add(list);}return ret;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/864732.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BIOS设置与系统分区

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 目录 一BIOS 1破解密码的前提 2B…

《植物大战僵尸融合版》1.0预览版下载以及安装教程

游戏介绍 《植物大战僵尸融合版》是一款基于经典游戏《植物大战僵尸》的创新改版&#xff0c;由B站UP主蓝飘飘fly开发。这款游戏的特色在于引入了植物融合系统&#xff0c;允许玩家将两种不同的植物合体&#xff0c;创造出具有新特性的植物&#xff0c;从而带来全新的策略对战…

Canvas:掌握贝塞尔曲线与封装路径

想象一下&#xff0c;用几行代码就能创造出如此逼真的图像和动画&#xff0c;仿佛将艺术与科技完美融合&#xff0c;前端开发的Canvas技术正是这个数字化时代中最具魔力的一环&#xff0c;它不仅仅是网页的一部分&#xff0c;更是一个无限创意的画布&#xff0c;一个让你的想象…

文件夹加密软件推荐,这款软件满足各种加密需求

文件夹加密是保护电脑数据安全的重要方式&#xff0c;而在加密文件夹时&#xff0c;我们需要选择专业的文件夹加密软件。下面小编就为大家推荐一款优秀的文件夹加密软件&#xff0c;满足你的各种文件夹加密需求。 文件夹加密超级大师 由于文件夹储存的数据不同&#xff0c;比如…

MCU 是什么?一文了解MCU 产业

MCU&#xff08;Microcontroller Unit&#xff09;&#xff0c;中文名为“微控制器单元”、“单片微型计算机”。MCU 将中央处理器&#xff08;CPU&#xff09;、内存&#xff08;RAM&#xff09;、输入 / 输出界面&#xff08;I/O&#xff09;等等一大堆东西&#xff0c;全部整…

数据结构——带环链表、循环队列问题

1.带环链表问题 1.1给定一个链表判断其是否带环 解决思路&#xff1a;利用快慢指针法&#xff0c;快指针一次走两步慢指针一次走一步&#xff0c;从链表起始位置遍历链表&#xff0c;如果链表带环&#xff0c;则快慢指针一定会在环中相遇&#xff0c;否则快指针先到达链表末尾…

Kafka-服务端-副本同步-源码流程

杂 在0.9.0.0之前&#xff0c;Kafka提供了replica lag.max.messages 来控制follower副本最多落后leader副本的消息数量&#xff0c;follower 相对于leader 落后当超过这个数量的时候就判定该follower是失效的&#xff0c;就会踢出ISR&#xff0c;这里的指的是具体的LEO值。 对…

软考高级之系统分析师及系统架构设计师备考过程记录

0x00 前言 考了两次系分&#xff0c;一次架构&#xff0c;今年系分终于上岸。 在此记录备考过程和一些体会 先说我自己的情况&#xff0c;我本硕都是计算机科班出身&#xff0c;本科的时候搞Java后端开发&#xff0c;硕士搞python和深度学习&#xff0c;但工作之后就基本没碰过…

洞鉴-产品部署及其功能

网络策略&#xff1a;安装&#xff1a; 资源准备 ⼀、系统安装包 https://chaitin-release.oss-cnbeijing.aliyuncs.com/release%2Ff%2F66600aac66bcea13c086319c?Expires1719310707 &OSSAccessKeyIdLTAI5tBpSz7iLYLH51NrVx22&SignaturesOpuVYuKpm9ZBoEzfwiRlJ fLrhQ…

STM32 HAL库读取ID

在stm32f1xx_hal.c文件中由读取ID号的子函数&#xff0c;不同单片机的UID_BASE不同&#xff0c;本单片机用的是STM32F103CBT6,跳转之后可以看到地址为&#xff1a;0x1FFFF7E8 在程序中只需定义一个数组调用读取ID的函数即可 uint32_t UID[3]; while(1) { UID[0] HAL_GetUIDw0…

C盘清理和管理

本篇是C盘一些常用的管理方法&#xff0c;以及定期清理C盘的方法&#xff0c;大部分情况下都能避免C盘爆红。 C盘清理和管理 C盘存储管理查看存储情况清理存储存储感知清理临时文件清理不需要的 迁移存储 磁盘清理桌面存储管理应用存储管理浏览器微信 工具清理 C盘存储管理 查…

VUE3+ AntV Select 选择器:mode=“multiple“和mode=“tags“的区别是什么

文章目录 VUE3 AntV Select 选择器&#xff1a;mode"multiple"和mode"tags"的区别是什么一、解释二、对比演示 VUE3 AntV Select 选择器&#xff1a;mode"multiple"和mode"tags"的区别是什么 一、解释 “mode” 是一个参数&#xff…

SpringSecurity中文文档(Servlet Persisting Authentication)

Persisting Authentication 用户第一次请求受保护的资源时&#xff0c;系统会提示他们输入凭据。提示凭据的最常见方法之一是将用户重定向到登录页。对于请求受保护资源的未经身份验证的用户&#xff0c;总结的 HTTP 交换可能如下所示: Example 1. Unauthenticated User Requ…

VBA字典与数组第十六讲:行、列数不相同的数组间运算规律

《VBA数组与字典方案》教程&#xff08;10144533&#xff09;是我推出的第三套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;字典是VBA的精华&#xff0c;我要求学员必学。7.1.3.9教程和手册掌握后&#xff0c;可以解决大多数工作中遇到的实际问题。…

【SpringCloud】Config源码解析

config是一个微服务配置组件&#xff0c;为微服务提供集中化的配置管理服务。config包含服务端和客户端&#xff0c;客户端在启动服务时从服务端拉取配置信息&#xff0c;服务端响应客户端的请求提供具体的配置。本章分析config组件配置信息的拉取过程 1、config服务端 服务端…

一键AI抠图太方便啦!不会ps也能成为修图大师

引言 在数字生活中&#xff0c;抠图技能已成为一项日常且必不可少的技能。无论是需要更换证件照的背景色&#xff0c;还是想要将图像中的主体精确分离。 但并非所有人都精通Photoshop&#xff0c;而且对于简单的任务来说&#xff0c;使用Photoshop可能显得过于复杂。因此&…

1077 韩信点兵

这是一个中国剩余定理的问题。中国剩余定理是数论中的一个定理&#xff0c;它给出了一组同余方程的解的存在性和唯一性。在这个问题中&#xff0c;我们需要找到一个数&#xff0c;使得它对给定的每个质数取余的结果等于给定的余数。 以下是一个使用C实现的解决方案&#xff1a…

Spark2.0

目录 10.3 Spark运行架构 10.3.1 基本概念 10.3.2 架构设计 ​编辑 10.3.3 Spark运行基本流程 Spark运行架构特点 10.3 Spark运行架构 10.3.1 基本概念 RDD &#xff1a;是 Resillient Distributed Dataset &#xff08;弹性分布式数据集&#xff09;的简称&#xff0c;是分…

【Llama 2的使用方法】

Llama 2是Meta AI&#xff08;Facebook的母公司Meta的AI部门&#xff09;开发并开源的大型语言模型系列之一。Llama 2是在其前身Llama模型的基础上进行改进和扩展的&#xff0c;旨在提供更强大的自然语言处理能力和更广泛的应用场景。 以下是Llama 2的一些关键特性和更新点&am…

git主机仓库地址迁移后 git提交代码报错

找到本地电脑的文件known_hosts 2.在代码中git pull 此时终端会有提示 输入ye enter提交便成功了