代码随想录刷题笔记 DAY12 | 二叉树的理论基础 | 二叉树的三种递归遍历 | 二叉树的非递归遍历 | 二叉树的广度优先搜索

Day 12

01. 二叉树的理论基础


1.1 二叉树的种类
  1. 满二叉树:除了叶子节点以外,每个节点都有两个子节点,整个树是被完全填满的
  2. 完全二叉树:除了底层以外,其他部分是满的,底部可以不是满的但是必须是从左到右连续的

image-20240123141309343 在这里插入图片描述

  1. 二叉搜索树:节点是有顺序的,可查找的
  2. 平衡二叉搜索树:左子树和右子树的高度值不能超过 1

比如上面的树,比 6 大的在左边,小的在右边,且每个节点都是这样的,有顺序的,查询时间复杂度为 logn

很显然我们中间节点的选择会影响左子树和右子树的高度,左子树和右子树高度不超过 1 的被称为平衡二叉搜索树。

1.2 二叉树的存储方式

链式存储:顺序存储即采用链表的方式来存储,是我们常用的存储方式,即使用链表的方式来存储:一个树节点分别有它的左节点和右节点,他们的左节点和右节点又连接着其他的树节点。

顺序存储(了解即可):

我们给每个节点编号,节点的左节点就是 2n 右节点就是 2n - 1

1.3 遍历方式

深度优先搜索:即先一路搜索到最底部再递归返回,我们常见的前序、中序、后序遍历都是深度优先搜索。

可以使用递归的方式和非递归的方式,迭代的方式有可能在面试中会考察

广度优先搜索:一层一层的去遍历,使用队列先进先出的特性实现广度优先搜索。

1.4 定义方式
class TreeNode {int val;TreeNode rightNode;TreeNode leftNode;
}

和我们上面说的相同,和链表的定义方式相同,但分为左子树和右子树。

02. 二叉树的递归遍历


​ 这一部分应该是很多朋友一开始学算法十分困惑的一个点,总是想不明白递归的题也看不明白递归的解法,我大一刚开始刷算法的时候也是这样,当时真是一入递归深似海,之后有一篇文章启发了我

东哥带你刷二叉树(纲领篇)

拉不拉东老师的这篇关于二叉树文章。

我来借用一下里面的思维方式

我们之所以无法理解递归是因为我们还是利用之前读代码和写代码的方式:“将自己的脑子当作计算机来执行一遍代码”,这在前面那些简单的顺序结构的题目中当然是可行而且有效的,但当我们解决的题目复杂起来,就比如现在的二叉树遍历的题目,我们的脑子能装下几个栈呢?能跑几层递归呢?

所谓理解和读懂递归就是要将它当作自己编写代码、解决问题的一种工具,而不是尝试去用脑子执行它,弄懂它的执行步骤,我们先来看一段二叉树 前序 遍历的代码

class Solution {List<Integer> res = new ArrayList();public List<Integer> preorderTraversal(TreeNode root) {reverse(root);return res;}public void reverse(TreeNode root) {if (root == null) {return;}res.add(root.val);reverse(root.left);reverse(root.right);}
}

我们将重点放在第二段的代码上,如果单单的问你我不看递归,我这一次做了什么?

  1. 首先我先判断当前的节点是否为空节点,这就是我们常说的递归的出口
  2. 然后我们将当前节点的值放到了 reslist 中作为结果
  3. 然后我们去递归遍历左子树
  4. 然后我们去递归遍历右子树

看我们上面的图是不是非常熟悉,这不就是前序遍历的遍历顺序吗?先中间再左边再右边,前序遍历无非就是对每个节点执行如上的相同的操作,那如何对每个节点操作呢?

递归的作用就是 帮助我们为每一个节点做相同的操作

我们只需要关注一个节点做的事情然后写到递归中,让递归帮我们去执行即可。

所有的递归都可以套用二叉树的模型来理解,我们知道,二叉树除了前序遍历,还有后序遍历和中序遍历:

我们每次进入一个节点都可以分为三个位置:

  1. 前序位置
  2. 中序位置
  3. 后序位置

对应着上图中的 1 2 3 三个部分

前序中的操作即是我们进入这个节点后立马执行的操作,这不就是我们上面的前序遍历吗?进入节点后立马输出

中序就是在节点中执行的结果,即上图中的 2 ,这不就是左子树返回东西的地方吗?

那中序遍历的代码该怎么写?

    public void reverse(TreeNode root) {if (root == null) {return;}reverse(root.left);res.add(root.val);reverse(root.right);}
}

到这里是不是对递归主键的清晰起来了?

后序遍历的代码我们也可以很轻松的写出来

    public void reverse(TreeNode root) {if (root == null) {return;}reverse(root.left);res.add(root.val);reverse(root.right);}
}

如果上面的内容都能看懂,那么恭喜你已经解决了力扣上的三道题目:

No.144 二叉树的前序遍历

No.94 二叉树的中序遍历

No.145 二叉树的后序遍历

这里我们来看一个小小的问题:使用递归实现链表的倒序输出

如果将上面的部分看懂,那这道题相信对你来说很简单了

链表就是简化的二叉树,上面的每一个节点就可以通过上面的思路来处理

链表没有中序位置,只有前序和后序,既然要倒序输出链表,那我们要考虑的是我们的输出语句放在哪里

非常简单的二选一题目,肯定是放在后序的位置,呈现在代码中就是这样的:

public void reverse(ListNode head) {if (head == null) {return;}reverse(head.next);System.out.println(head.val);
}

03. 二叉树的非递归遍历

3.1 非递归实现前序遍历

二叉树的递归遍历上面已经提到过了,代码实现也非常容易,但如果让我们使用非递归的方式来模拟前序遍历就比较困难了。

递归的实现是通过栈来完成的,所以我们自然的去想使用栈来模拟前序遍历,之前刷关于栈的题目的时候总结过,一道题能否用栈去解决关键在于能否满足先进后出的特点。

前序遍历是对 每个节点 进行 中 左 右 顺序的遍历,我们要对每个节点都进行这种操作,就会出现将节点放入又取出来,就会有先进后出的特性。

比如我们来模拟一下对这个二叉树的遍历

step 1

在这里插入图片描述

step2

在这里插入图片描述

step3

step4

和上面提到的一样,我们不用去模拟整个遍历的过程,我们只需要对每个节点进行的操作熟悉即可,再来总结一下上面的步骤:

  1. 将节点放入栈中
  2. 取出节点,存入结果数组
  3. 将节点的左节点放入栈中
  4. 将节点的右节点放入栈中

这样我们就用 非递归 的方式实现了前序遍历

这边给出代码,题目还是

No.144 二叉树的前序遍历

/*** 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 {Stack<TreeNode> stack = new Stack<>(); // 栈List<Integer> res = new ArrayList<>(); // 结果数组public List<Integer> preorderTraversal(TreeNode root) {stack.push(root);// 在 while 中持续执行上面提到的顺序while(!stack.isEmpty()) {TreeNode node = stack.pop();if (node != null) {res.add(node.val); stack.push(node.right);stack.push(node.left);}}return res;}
}
3.2 非递归实现二叉树的后序遍历

后序遍历的顺序是 左 右 中,而我们上面前序遍历的顺序是 中 左 右,如果我们入栈的时候先放入 左节点,也就是让右节点先弹出,遍历的顺序就变成了 中 右 左,这不就是后序遍历的逆序吗?

对入栈的元素进行上述处理后,再反转数组就可以实现后序遍历的效果

No.145 二叉树的后序遍历

/*** 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 {Stack<TreeNode> stack = new Stack<>(); // 栈List<Integer> res = new ArrayList<>(); // 结果数组public List<Integer> postorderTraversal(TreeNode root) {stack.push(root);// 在 while 中持续执行上面提到的顺序while(!stack.isEmpty()) {TreeNode node = stack.pop();if (node != null) {res.add(node.val); stack.push(node.left);  // 先放入右节点stack.push(node.right);}}Collections.reverse(res);return res;}
}
3.3 非递归实现中序遍历

中序遍历的顺序是 右 中 左,但这不像上面的后序遍历可以通过调整顺序再倒置的方式解出。

对于这种遍历顺序和输出次序不同的方式,给出另一个思路,可以通过指针加栈的方式

因为中序遍历是深度优先遍历,我们输出的第一个元素是遍历到左边最底部再输出的,所以我们指定一个指针一直持续向左遍历,直到 current.left 指向 null 的时候,也就是所有的左节点全都遍历完,这就是我们要输出的第一个元素,此时将栈中的元素弹出放入数组中。

然后我们来单独看 current 此时指向这个节点,这是我们要 输出 的第一个节点,再次来回顾一下中序遍历的过程

  1. 进入节点一直向左遍历
  2. 左边没有元素的时候直接输出当前节点
  3. 向右边遍历,对右边的节点执行相同的操作

当我们将当前的节点放入结果数组的时候,就已经执行完了前两个步骤,接下来就是去遍历右节点,并且对右节点执行相同的操作。

这道题的重点仍然是将中心放在某一个节点上,因为我们用栈记录了遍历的节点,通过弹栈就可以实现对每个节点实现相同的操作,接下来只需要将思路翻译成代码即可。

/*** 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 {List<Integer> res = new ArrayList();TreeNode current;Stack<TreeNode> stack = new Stack<>();public List<Integer> inorderTraversal(TreeNode root) {if (root == null){return res;}current = root;while (current != null || !stack.isEmpty()) {if (current != null) {stack.push(current);current = current.left;} else {current = stack.pop();res.add(current.val);   current = current.right;           }  }return res;}
}

04. 二叉树的广度优先搜索

层序遍历需要借助队列来实现,我们将每一层的元素一个个放到队列中,再逐一输出即可

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

重要的就是要区分每一层有哪些元素,因为下一层元素的 add 是可以通过上一层元素弹出后再将其左右节点加入来实现的,所以我们需要一个变量 size 来记录每一层的元素个数。

利用 for 循环来输出每层的元素,此时 栈内剩下的元素 便是下一层的所有元素,此时更新我们的 size ,比如上图中的第二层中的元素遍历完后,栈内剩余的元素就是第三层的元素

No.102 二叉树的层序遍历

/*** 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 {Queue<TreeNode> queue = new LinkedList<>();List<List<Integer>> res = new ArrayList<>();public List<List<Integer>> levelOrder(TreeNode root) {if (root == null) {return res;}int size = 1; // 记录每层的元素个数queue.add(root);while (!queue.isEmpty()) {List<Integer> tempList = new ArrayList<>();// 对每一层进行遍历for (int i = size; i > 0; i--) {TreeNode tempNode = queue.poll();// 弹出队列中的元素if (tempNode != null) {tempList.add(tempNode.val);queue.add(tempNode.left);queue.add(tempNode.right);}       }if (!tempList.isEmpty()){res.add(new ArrayList(tempList));}size = queue.size();// 更新 size 的长度}return res;}
}

1

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

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

相关文章

数据结构之受限线性表

受限线性表 对于一般线性表&#xff0c;虽然必须通过遍历逐一查找再对目标位置进行增、删和查操作&#xff0c;但至少一般线性表对于可操作元素并没有限制。说到这里&#xff0c;大家应该明白了&#xff0c;所谓的受限线性表&#xff0c;就是可操作元素受到了限制。 受限线性表…

【Web前端开发基础】CSS3之Web字体、字体图标、平面转换、渐变

CSS3之Web字体、字体图标、平面转换、渐变 目录 CSS3之Web字体、字体图标、平面转换、渐变一、Web字体1.1 Web字体概述1.2 字体文件1.3 font-face 规则 二、字体图标2.1 字体图标2.2 字体图标的优点2.3 图标库2.4 下载字体包2.5 字体图标的使用步骤2.6 字体图标使用注意点2.7 上…

「 典型安全漏洞系列 」06.路径遍历(Path Traversal)详解

引言&#xff1a;什么是路径遍历&#xff1f;如何进行路径遍历攻击并规避常见防御&#xff1f;如何防止路径遍历漏洞。 1. 简介 路径遍历&#xff08;Path Traversal&#xff09;是一种安全漏洞&#xff0c;也被称为目录遍历或目录穿越、文件路径遍历。它发生在应用程序未正确…

mysql生成最近24小时整点/最近30天/最近12个月时间临时表

文章目录 生成最近24小时整点生成最近30天生成最近12个月 生成最近24小时整点 SELECT-- 每向下推1行, i比上次减去1b.*, i.*,DATE_FORMAT( DATE_SUB( NOW(), INTERVAL ( -( i : i - 1 ) ) HOUR ), %Y-%m-%d %H:00 ) AS time FROM-- 目的是生成12行数据( SELECTa FROM( SELECT…

搭建《幻兽帕鲁》服务器需要怎样配置的云服务器?

随着《幻兽帕鲁》这款游戏的日益流行&#xff0c;越来越多的玩家希望能够在自己的服务器上体验这款游戏。然而&#xff0c;搭建一个稳定、高效的游戏服务器需要仔细的规划和配置。本文将分享搭建《幻兽帕鲁》服务器所需的配置及搭建步骤&#xff0c;助力大家获得更加畅快的游戏…

搭建k8s集群实战(一)系统设置

1、架构及服务 Kubernetes作为容器集群系统&#xff0c;通过健康检查重启策略实现了Pod故障自我修复能力&#xff0c;通过调度算法实现将Pod分布式部署&#xff0c;并保持预期副本数&#xff0c;根据Node失效状态自动在其他Node拉起Pod&#xff0c;实现了应用层的高可用性。 …

树的学习day01

树的理解 树是一种递归形式的调用 树是由于多个结点组成的有限集合T 树中有且仅有一个结点称为根 当结点大于1的时候&#xff0c;往往其余的结点为m个互不相交的有限个集合T1,…,Tm&#xff0c;每个互不相交的有限集合本身右是一棵树&#xff0c;称为这个根的子树 空树也是树 关…

选现货白银投资划不划算?

可以肯定的是选择现货白银投资是划算的&#xff0c;但投资者需要有足够的知识和经验&#xff0c;以及对市场的敏锐观察力。只有这样&#xff0c;投资者才能在现货白银投资中获取收益。在投资市场上&#xff0c;白银作为一种特殊的投资品种&#xff0c;一直以来都备受投资者们的…

JUC-CAS

1. CAS概述 CAS(Compare ans swap/set) 比较并交换&#xff0c;实现并发的一种底层技术。它将预期的值和内存中的值比较&#xff0c;如果相同&#xff0c;就更新内存中的值。如果不匹配&#xff0c;一直重试&#xff08;自旋&#xff09;。Java.util.concurrent.atomic包下的原…

Redis - redis.windows.conf配置文件及RDB和AOF数据持久化方案

Redis - redis.windows.conf配置文件及RDB和AOF数据持久化方案 Redis的高性能是由于其将所有数据都存储在了内存中&#xff0c;为了使Redis在重启之后仍能保证数据不丢失&#xff0c;需要将数据从内存中同步到硬盘中&#xff0c;这一过程就是持久化。 Redis支持两种方式的持久化…

【51单片机】点亮第一个LED灯

目录 点亮第一个LED灯单片机 GPIO 介绍GPIO 概念GPIO 结构 LED简介软件设计点亮D1指示灯LED流水灯 橙色 点亮第一个LED灯 单片机 GPIO 介绍 GPIO 概念 GPIO&#xff08;general purpose intput output&#xff09; 是通用输入输出端口的简称&#xff0c; 可以通过软件来控制…

数据库查询练习

数据准备 #建学生信息表student create table student ( sno varchar(20) not null primary key, sname varchar(20) not null, ssex varchar(20) not null, sbirthday datetime, class varchar(20) ); #建立教师表 create table teacher ( tno varchar(20) not null primary…

仿真机器人-深度学习CV和激光雷达感知(项目2)day04【简单例程】

文章目录 前言简单例程运行小海龟仿真启动节点查看计算图发布 Topic调用 Serviece 用 Python 发布和接收 Topic创建工作空间创建功能包&#xff0c;编译编写 Topic Publisher 节点编写 Topic Subscriber 节点运行节点 自定义消息类型用 Python 注册和调用 Serviece新建功能包在…

分类预测 | Matlab实现LSTM-Attention-Adaboost基于长短期记忆网络融合注意力机制的Adaboost数据分类预测/故障识别

分类预测 | Matlab实现LSTM-Attention-Adaboost基于长短期记忆网络融合注意力机制的Adaboost数据分类预测/故障识别 目录 分类预测 | Matlab实现LSTM-Attention-Adaboost基于长短期记忆网络融合注意力机制的Adaboost数据分类预测/故障识别分类效果基本描述程序设计参考资料 分类…

鸿蒙开发案例002

1、目标需求 界面有增大字体按钮&#xff0c;每次点击增大字体按钮&#xff0c;“Hello ArkTS”都会变大 2、源代码 Entry Component struct Page {textValue: string Hello ArkTSState textSize: number 50myClick():void{this.textSize 4}build() {Row() {Column() {//…

stm32 FOC 电机介绍

今年开始学习foc控制无刷电机&#xff0c;这几天把所学整理一下&#xff0c;记录一下知识内容。 前言: 为什么要学习FOC? 1.电机控制是自动化控制领域重要一环。 2.目前直流无刷电机应用越来越广泛&#xff0c;如无人机、机械臂、云台、仿生机器人等等。 需要什么基础&…

基于Springboot的大学生心理健康管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的大学生心理健康管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体…

MySQL安装及可视化工具SQLyog下载

编程如画&#xff0c;我是panda&#xff01; 最近学习Web开发的时候要用到数据库&#xff0c;一开始下载的ZIP版本的&#xff0c;还得修改配置文件&#xff0c;挺麻烦的&#xff0c;后来发现可以直接使用msi版的安装包疯狂next&#xff0c;所以就出一期教程。 前言 MySQL 是一…

Database__进阶

文章目录 &#x1f60a; 作者&#xff1a;Lion J &#x1f496; 主页&#xff1a; https://blog.csdn.net/weixin_69252724?spm1000.2115.3001.5343 &#x1f389; 主题&#xff1a; 数据库mysql&#xff08;高级部分&#xff09; ⏱️ 创作时间&#xff1a;2024年01月24…

TortoiseSVN源码安装与迁移全攻略

一、前言 随着版本控制系统的普及&#xff0c;越来越多的开发者和团队开始使用SVN&#xff08;Subversion&#xff09;来管理代码。本文将详细介绍TortoiseSVN的源码安装及迁移过程&#xff0c;帮助您轻松掌握这一版本控制工具。 二、TortoiseSVN源码安装 依赖环境安装&…