代码随想录训练营 Day13打卡 二叉树 part01 递归遍历 迭代遍历 层序遍历

代码随想录训练营 Day13打卡 二叉树 part01

一、 二叉树理论基础

二叉树是一种重要的数据结构,用于表示具有层次关系的数据。二叉树的每个节点最多有两个子节点,通常称为左子节点和右子节点。

种类

  1. 普通二叉树: 节点最多有两个子节点,没有其他特定规则。
  2. 满二叉树: 所有非叶子节点都拥有两个子节点,并且所有叶子节点都在同一层上。
  3. 完全二叉树: 除最后一层外,每一层都是满的,并且最后一层的节点都尽可能地集中在左侧。
  4. 二叉搜索树(BST): 每个节点的左子树只包含键小于节点键的节点,右子树只包含键大于节点键的节点。
  5. 平衡二叉树(AVL树): 任何节点的两个子树的高度差不超过1。
  6. 红黑树: 一种自平衡的二叉搜索树,它有额外的属性和特定的规则,以确保树的高度大致平衡。

存储方式

二叉树可以用多种方式存储,最常见的包括:

  1. 链式存储: 每个节点包含数据和两个指向子节点的引用(左和右)。
  2. 顺序存储: 使用数组存储二叉树的节点。对于数组中位置为 i 的节点,其左子节点位于 2i+1,右子节点位于 2i+2,父节点位于
    (i-1)/2。

遍历方式

二叉树的遍历分为三种主要类型:

  1. 前序遍历(Preorder): 先访问根节点,然后递归地做前序遍历左子树,再递归地做前序遍历右子树。
  2. 中序遍历(Inorder): 先递归地做中序遍历左子树,访问根节点,然后递归地做中序遍历右子树。
  3. 后序遍历(Postorder): 先递归地做后序遍历左子树,然后递归地做后序遍历右子树,最后访问根节点。
  4. 层次遍历(Level-order): 按照树的层次从上到下、从左到右遍历,通常使用队列来实现。

Python中二叉树的实现

class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = right

二、 递归遍历

递归算法的三个要素

  1. 参数与返回值: 定义递归函数的参数及其返回值。
  2. 终止条件: 定义递归应停止的条件。
  3. 单层递归逻辑: 定义每层递归需要执行的操作及如何递归调用自身。

遵循这三个步骤可以帮助你编写出清晰且正确的递归算法。

前序遍历(Preorder Traversal)

前序遍历的顺序是根节点 → 左子树 → 右子树。

class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = rightclass Solution:def preorderTraversal(self, root: TreeNode) -> List[int]:res = []def dfs(node):# 终止条件:节点为空if not node:return# 单层递归逻辑:访问根,然后左子树,然后右子树res.append(node.val)   # 访问根节点dfs(node.left)         # 递归访问左子树dfs(node.right)        # 递归访问右子树dfs(root)return res

中序遍历(Inorder Traversal)

中序遍历的顺序是左子树 → 根节点 → 右子树。

class Solution:def inorderTraversal(self, root: TreeNode) -> List[int]:res = []def dfs(node):# 终止条件:节点为空if not node:return# 单层递归逻辑:先左子树,再访问根,最后右子树dfs(node.left)         # 递归访问左子树res.append(node.val)   # 访问根节点dfs(node.right)        # 递归访问右子树dfs(root)return res

后序遍历(Postorder Traversal)

后序遍历的顺序是左子树 → 右子树 → 根节点。

class Solution:def postorderTraversal(self, root: TreeNode) -> List[int]:res = []def dfs(node):# 终止条件:节点为空if not node:return# 单层递归逻辑:先左子树,再右子树,最后访问根dfs(node.left)         # 递归访问左子树dfs(node.right)        # 递归访问右子树res.append(node.val)   # 访问根节点dfs(root)return res

文章讲解
视频讲解

三、 迭代遍历

为什么可以使用迭代方式实现二叉树遍历?

递归本质上是通过函数 调用栈 来保存暂停点的数据,然后在返回时继续执行。
每个递归调用保存当前函数的局部变量和必要的状态信息,使得函数能够在执行完成后返回到正确的位置继续执行。
通过使用栈结构,我们可以显式地模拟这个过程,自己管理栈中元素的入栈和出栈,从而控制程序的执行流程。

前序遍历(迭代法)

前序遍历的顺序是:中 - 左 - 右。要使用迭代法实现前序遍历,你可以遵循以下步骤:

  1. 创建一个空栈,首先将根节点压入栈中。

  2. 循环直到栈为空:

    · 弹出栈顶元素,访问该节点。
    · 将该节点的右孩子压入栈中(如果有)。
    · 将该节点的左孩子压入栈中(如果有)。

这样的处理顺序确保了每次从栈中弹出的是下一个要访问的节点,因为栈是后进先出(LIFO)的数据结构,所以要先处理的节点后入栈。
动画如下:
在这里插入图片描述

# 前序遍历-迭代-LC144_二叉树的前序遍历
class Solution:def preorderTraversal(self, root: TreeNode) -> List[int]:# 根结点为空则返回空列表if not root:return []stack = [root]result = []while stack:node = stack.pop()# 中结点先处理result.append(node.val)# 右孩子先入栈if node.right:stack.append(node.right)# 左孩子后入栈if node.left:stack.append(node.left)return result

后序遍历(迭代法)

再来看后序遍历,先序遍历是中左右,后序遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了,如下图:
在这里插入图片描述

# 后序遍历-迭代-LC145_二叉树的后序遍历
class Solution:def postorderTraversal(self, root: TreeNode) -> List[int]:if not root:return []stack = [root]result = []while stack:node = stack.pop()# 中结点先处理result.append(node.val)# 左孩子先入栈if node.left:stack.append(node.left)# 右孩子后入栈if node.right:stack.append(node.right)# 将最终的数组翻转return result[::-1]

中序遍历(迭代法)

中序遍历的迭代实现需要精心处理访问和处理元素的顺序,因为中序遍历的顺序是左中右,这导致了处理节点的顺序(将节点值加入结果数组)与节点的访问顺序(遍历到该节点)不一致。

那么在 使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素

动画如下:
在这里插入图片描述
实现思路

  1. 栈的使用:

    栈用来暂存还未处理的节点,这主要是因为在深入左子树过程中,我们还没处理父节点及其右子树。

  2. 访问与处理分离:

    使用一个指针 cur(当前节点)来进行树的遍历,遵循中序遍历的规则(左-中-右)。
    当 cur 不为空,表示还有左子树需要进一步深入访问,所以将 cur 入栈并转向左子节点。
    当 cur 为空,表示左边已到底,此时从栈中取出节点进行处理(输出节点值),然后转向右子树。

  3. 迭代循环的条件:

    当 cur 不为空或栈不为空时,循环继续。这是因为:
    cur 不为空表示当前节点或其左子树还未完全处理。
    栈不为空表示还有一些节点的右子树需要处理。

class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = rightclass Solution:def inorderTraversal(self, root: TreeNode) -> List[int]:if not root:return []stack = []  # 使用栈来追踪未处理的节点result = []  # 结果数组,用来存放按照中序遍历的节点值cur = root  # 使用 cur 指针来遍历树while cur or stack:if cur:# 深入遍历到左子树最深处,左子节点一直入栈stack.append(cur)cur = cur.leftelse:# 到达最左侧节点后,开始处理节点cur = stack.pop()  # 弹出待处理的节点result.append(cur.val)  # 将节点值加入结果数组cur = cur.right  # 转向处理右子树return result

通过上述迭代法,我们能够不使用递归(系统调用栈)而使用显式栈来控制中序遍历的流程,这样可以避免递归可能引起的栈溢出问题,并且对遍历过程有更明确的控制。

文章讲解
视频讲解1
视频讲解2

四、 层序遍历

层序遍历二叉树(也称为宽度优先遍历或广度优先遍历)是一种按照层次顺序遍历二叉树节点的方法。在这一遍历过程中,首先访问根节点,然后依次访问其子节点,再依次访问下一层的所有节点,依此类推,直到遍历完所有层。

为了实现层序遍历,我们通常使用队列这种数据结构,因为队列遵循先进先出(FIFO)的原则,这与层序遍历的逻辑相吻合。

使用队列实现二叉树广度优先遍历,动画如下:
在这里插入图片描述

下面是一个使用队列实现层序遍历的步骤说明:

  1. 初始化队列: 创建一个空队列。

  2. 入队根节点: 将根节点入队。

  3. 循环处理队列:

    循环条件:只要队列不为空,就一直处理。
    出队节点:从队列头部取出一个节点。
    访问节点:处理或访问该节点。
    入队子节点:将该节点的左右子节点(如果有)入队。

  4. 重复步骤3: 直到队列为空。

版本一 迭代法

层次遍历(或广度优先搜索)通常使用队列来实现。这种方法依靠队列的先进先出(FIFO)特性来保持节点的访问顺序。

from collections import dequeclass TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = rightclass Solution:def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:if not root:return []queue = deque([root])  # 初始化队列,首先加入根节点result = []  # 用于存储每一层的节点值while queue:level = []  # 存储当前层的所有节点值level_length = len(queue)  # 当前层的节点数for _ in range(level_length):cur = queue.popleft()  # 弹出队列前端的节点level.append(cur.val)  # 将当前节点的值加入到本层列表中# 如果当前节点有左子节点,将其加入队列if cur.left:queue.append(cur.left)# 如果当前节点有右子节点,将其加入队列if cur.right:queue.append(cur.right)result.append(level)  # 将当前层的结果加入到最终结果列表中return result

版本二 递归法

递归方法通过定义一个辅助函数来遍历树,这个辅助函数接受当前节点和节点的层级作为参数,层级用来确定当前节点值应该被添加到结果列表的哪个部分。

class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = rightclass Solution:def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:if not root:return []levels = []  # 用于存储所有层的节点值def traverse(node, level):# 如果当前层还没有创建列表,则新增一个空列表if len(levels) == level:levels.append([])# 将当前节点的值添加到对应层的列表中levels[level].append(node.val)# 递归处理左子节点,层级加一if node.left:traverse(node.left, level + 1)# 递归处理右子节点,层级加一if node.right:traverse(node.right, level + 1)traverse(root, 0)  # 从根节点的第0层开始遍历return levels
  • 迭代法 通过显式使用队列来按层次顺序遍历树的节点,适合于需要严格按照层序的场景。
  • 递归法 通过调用栈来实现层次遍历,更直观,但在深度很大的树中可能会导致栈溢出。

这两种方法各有优势,通常迭代法在实现树的层次遍历时更为常见和直接,递归法则更加简洁。根据具体情况选择合适的方法。

文章讲解
视频讲解

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

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

相关文章

Postman中的A/B测试实践:优化API性能的科学方法

Postman中的A/B测试实践:优化API性能的科学方法 在API开发和测试过程中,A/B测试是一种验证新功能或变更效果的有效方法。通过比较两个或多个版本(例如A版本和B版本)的性能,可以科学地评估变更的影响。Postman作为API测…

Hadoop3:HDFS的客户端工具Big Data Tools(IDEA版本)

1、安装插件 在Plugins里搜索Big Data Tools 安装完成后,重启IDEA 2、配置Windows环境 主要是配置Hadoop环境,否则无法通过插件远程连接HDFS 1、解压hadoop安装包 2、进入hadoop的bin目录 放入图中标红的两个文件 3、配置hadoop环境变量 新建HAD…

Java中“静态方法“之所以叫做“静态“的历史遗留问题

之所以叫做"静态",还要从C语言说起. C语言引入了static关键字. 早期,操作系统里,有一块内存区域,称为"静态内存区" 使用static修饰,变量就是在这个内存区域中的 后来系统不断演化,静态内存区,这个叫法就逐渐没有了,static就有了新的含义: 修饰全局变量,…

深入解析Eureka中的服务实例健康检查机制

引言 在微服务架构中,服务实例的健康检查是确保服务可用性的关键环节。Eureka作为Netflix开源的服务发现框架,提供了一套机制来监控服务实例的健康状态。本文将深入探讨Eureka中的服务实例健康检查是如何实现的,包括其原理、配置和实践应用。…

CAS5.3使用自定义获取更多的用户信息

玩过cas的小伙伴都知道CAS-Server本身是支持CAS和OAuth2.0两种协议的, 之前写过一篇使用OAuth2.0协议获取用户信息的文章,今天来介绍一下CAS协议怎么获取更多的用户信息 分析 OAuth2.0协议的用户信息是接口返回的, 因此我们修改接口的实现就可以, CAS获取用户信息都是从sessi…

虚拟局域网络(VLAN)详解

一、VLAN概述 VLAN(Virtual Local Area Network,虚拟局域网络)技术是一种将物理局域网在逻辑上划分为多个虚拟网络的技术。VLAN技术的出现,打破了传统物理网络的限制,使得网络管理更加灵活、高效。VLAN技术广泛应用于企业网络、数据中心、云计算环境等各个领域,为网络的…

这个暑假适合港澳游?免税额度提升、专属暑期活动、餐饮娱乐攻略

2024年7月1日免税额度的新政实施,在香港澳门消费的免税额度将达到15000元。这也让今年夏天的港澳旅游多了几分吸引力,如果有计划趁着这个暑假前往港澳旅游的话,这篇港澳游攻略一定能帮到你! 【民以食为天,旅游第一步—…

“多分辨率图网络”的模型:准确识别卵巢癌各种亚型

卵巢癌发病率居我国女性生殖系统恶性肿瘤第3位,死亡率居妇科恶性肿瘤之首,其特点是存在异质性的组织学亚型。最常见的五种亚型,占所有卵巢癌的90%,分别是高级别浆液性癌(HGSC)、低级别浆液性癌(…

项目实战--JUC之CompletableFuture温故

CompletableFuture温故 一、前言二、Future三、CompletableFuture3.1 CompletableFuture定义3.2 CompletableFuture使用场景3.3 CompletableFuture 常见操作3.3.1 创建CompletableFuture3.3.2 使用CompletableFuture3.3.3 异常处理3.3.4 注意事项 四、CompletableFuture处理工具…

阿里云创建的SpringBoot项目打包时找不到主清单属性

原因是因为maven配置文件中有一个打包属性配置 <configuration><mainClass>com.yang.ExamSystemApplication</mainClass><!-- 找到这个<skip>true</skip>标签关闭&#xff0c;或者属性改为false --><skip>true</skip> </c…

Hive环境搭建(Mysql数据库)

【实验目的】 1) 了解hive的作用 2) 熟练hive的配置过程&#xff08;Mysql数据库&#xff09; 【实验原理】 Hive工具中默认使用的是derby数据库&#xff0c;该数据库使用简单&#xff0c;操作灵活&#xff0c;但是存在一定的局限性&#xff0c;hive支持使用第三方数据库&…

第一百八十三节 Java IO教程 - Java目录事件、Java异步I/O

Java IO教程 - Java目录事件 当文件系统中的对象被修改时&#xff0c;我们可以监听watch服务以获取警报。 java.nio.file包中的以下类和接口提供watch服务。 Watchable接口WatchService接口WatchKey接口WatchEvent接口WatchEvent.Kind接口StandardWatchEventKinds类 可监视对…

FPGA开发——按键控制LED的实现

一、概述 在上一篇文章中我们学习了按键的相关消抖及其使用&#xff0c;在这篇文章当中我们就针对通过按键实现LED的控制。 1、按键原理图 2、基本框架 通过我们前面编写的按键消抖的文件和LED文件将按键和LED两个模块进行交互&#xff0c;从而达到按键控制LED的目的。 二、…

计算机毕业设计碾压导师Python+Django农产品推荐系统 农产品爬虫 农产品商城 农产品大数据 农产品数据分析可视化 PySpark Hadoop

基于Spark的农产品个性推荐系统 相关技术介绍: 1. Python Python是一种高级编程语言&#xff0c;具有简洁、易读、易学的特点&#xff0c;被广泛应用于Web开发、数据分析、人工智能等领域。 在此系统中&#xff0c;我们使用Python进行后端开发&#xff0c;利用其强大的语法…

Mysql 输出本月初至当前的全部天数

代码&#xff1a; SELECT DATE_FORMAT(DATE_ADD(CONCAT(DATE_FORMAT(CURDATE(), "%Y-%m-01")),INTERVAL (CAST(help_topic_id AS SIGNED)) DAY),%Y-%m-%d) as DATE FROMmysql.help_topic WHERE help_topic_id < TIMESTAMPDIFF(DAY, CONCAT(DATE_FORMAT(CURDATE…

【最优化方法】随笔 - 基本概念简单整理

文章目录 前言1.背景知识两个概念&#xff1a; 2.最优化问题2.1应用2.2 最优化问题的数学形式极小化目标函数可行区域可行解 2.3 举例说明2.4 最优化问题不同的类型2.5 一些概念 3.凸集和凸函数3.1 范数3.2 矩阵范数&#xff08;扩展&#xff09;3.3 凸集与凸函数凸集凸函数 写…

实时宽表建设

实时宽表建设是数据仓库&#xff08;Data Warehouse, 简称数仓&#xff09;中的一种重要建模技术&#xff0c;旨在提高数据查询和分析的效率。以下是对实时宽表建设的详细解释&#xff1a; 一、定义 实时宽表&#xff0c;顾名思义&#xff0c;是指具有较多字段&#xff08;列…

无人机像素经纬度识别

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

组件库 组件工程化管理探索

文章目录 组件库单组件版本控制打包组件在用户项目中注册组件项目可以直接在模版中使用引入的打包后的组件原因 每个组件独立的 package.json 文件的单组件包管理使用 lerna 或 yarn workspaces 进行统一管理添加组件文档、测试通过 CI/CD 流水线自动化这个流程 国际化换肤组件…

旅美钢琴学者何超东北巡演启幕,精湛演绎肖邦全套作品

7月26日、28日&#xff0c;旅美钢琴学者何超在长春和哈尔滨两地成功开启了他的暑期钢琴独奏音乐会东北巡演。在这两场演出中&#xff0c;何超为观众带来了全套肖邦的「谐谑曲」与「叙事曲」&#xff0c;以娴熟的技巧、惊人的记忆力和体力&#xff0c;将八首曲目一气呵成&#x…