算法刷题Day14 | 二叉树理论、递归遍历、迭代遍历、统一迭代

目录

  • 0 引言
  • 1 递归遍历
    • 1.1 前序遍历
    • 1.2 后序遍历
    • 1.3 中序遍历
  • 2 迭代遍历
    • 2.1 前序和后序
    • 2.2 中序

请添加图片描述

  • 🙋‍♂️ 作者:海码007
  • 📜 专栏:算法专栏
  • 💥 标题:算法刷题Day14 | 二叉树理论、递归遍历、迭代遍历、统一迭代
  • ❣️ 寄语:书到用时方恨少,事非经过不知难!

0 引言

继续加油!

1 递归遍历

  • 🎈 文档讲解
  • 🎈 视频讲解
  • 🎈 做题状态:简单

写好递归的三大步骤:

  1. 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数,并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
  2. 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
  3. 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。(对于树的遍历可以直接考虑只有三个节点的满二叉树的情况)

1.1 前序遍历

class Solution {
public:void recursion(TreeNode* root, vector<int>& data){// 确定参数和返回值// 确定递归终止条件// 确定单层遍历逻辑,也就是最简单的二叉树遍历的逻辑if (root == nullptr) return;data.push_back(root->val);recursion(root->left, data);recursion(root->right, data);}vector<int> preorderTraversal(TreeNode* root) {vector<int> result;recursion(root, result);return result;}
};

1.2 后序遍历

前中后遍历的递归写法很类似,只需要将数据保存的顺序放到最后就行。

   void recursion(TreeNode* root, vector<int>& data){// 确定参数和返回值// 确定递归终止条件// 确定单层遍历逻辑,也就是最简单的二叉树遍历的逻辑if (root == nullptr) return;recursion(root->left, data);recursion(root->right, data);data.push_back(root->val);}

1.3 中序遍历

   void recursion(TreeNode* root, vector<int>& data){// 确定参数和返回值// 确定递归终止条件// 确定单层遍历逻辑,也就是最简单的二叉树遍历的逻辑if (root == nullptr) return;recursion(root->left, data);data.push_back(root->val);recursion(root->right, data);}

2 迭代遍历

  • 🎈 文档讲解
  • 🎈 视频讲解
  • 🎈 做题状态:

1. 递归的底层实现

通常情况下,递归的底层实现会利用函数调用栈。每次递归调用都会在函数调用栈上创建一个新的栈帧,用于存储该次函数调用的局部变量、参数值和返回地址等信息。当递归调用结束时,对应的栈帧会被销毁,控制权返回到上一级调用的位置。

这种递归调用过程会导致函数调用栈的深度不断增加,直到达到系统所能容纳的最大深度(通常由系统栈的大小限制),如果递归的深度超过了这个限制,就会发生栈溢出错误。

因此,在实际编程中,需要注意控制递归的深度,尤其是在处理大规模数据或者递归深度较深的情况下,以避免栈溢出的问题。

2. 迭代是什么?为什么叫做迭代的遍历方式?

在计算机科学中,“迭代”(iteration)通常指的是通过重复的过程来逐步接近所需的结果。在二叉树的迭代遍历中,“迭代"指的是使用循环而不是递归来遍历树的节点。这种方法通常会利用栈或队列等数据结构来模拟递归过程中的函数调用栈,以便在不使用递归函数调用的情况下实现遍历。因此,虽然我们仍然按照树的结构遍历节点,但我们没有使用递归函数调用,而是通过在循环中模拟这些调用来完成遍历。因此,这种遍历方式被称为"迭代遍历”。

3. 迭代就是模拟递归的底层实现

迭代方法通常会模拟递归的底层实现,但是使用显式的数据结构(比如栈或队列)来管理状态,而不是依赖系统的函数调用栈。通过手动管理状态,迭代方法能够避免递归调用带来的额外开销,例如函数调用的内存消耗和栈溢出的风险。

迭代法通常需要使用循环结构来模拟递归调用的过程,并且需要适当地更新状态以确保迭代的终止条件得以满足。虽然这可能会增加代码的复杂性,但通常可以提高算法的效率和可控性。

总之,迭代法可以视为一种更灵活、更可控的方式来实现递归算法,尤其是在需要处理大规模数据或者避免栈溢出的情况下。

2.1 前序和后序

循环内处理的是三个节点,根节点、右节点、左节点。
初始化栈的时候是将根节点入栈。然后循环内是先将栈顶元素弹出(弹出之前别忘记先将值保存到一个变量中,不然我们就访问不到左右节点了),然后判断刚才弹出节点的右子树是否为空,不为空则将右节点入栈,再判断左节点是否为空,不为空则左节点入栈。然后到了下一个循环,下一个循环就是现将栈顶节点弹出,然后判断其左右节点。
循环终止条件:栈为空。

下面是前序迭代的代码

class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {// 使用栈容器,模拟递归调用的函数栈vector<int> result;if (root == nullptr) return result;stack<TreeNode*> node;node.push(root);while(!node.empty()){// 栈顶数据存下来,然后弹出栈result.push_back(node.top()->val);TreeNode* temp = node.top();node.pop();// 然后判断刚才弹出的栈顶元素的右子树是否为空,若不为空,则压栈if(temp->right != nullptr){node.push(temp->right);}// 然后判断刚才弹出的栈顶元素的左子树是否为空,若不为空,则压栈if(temp->left != nullptr){node.push(temp->left);}} return result;}
};

总结:循环内所做的事情:先将栈顶元素弹出,然后判断栈顶元素是否有右节点,如果有则压栈,再去判断栈顶元素的左节点是否为空,不为空则压栈。

疑惑点:前序遍历是 中左右,为什么压栈顺序是 中右左,因为栈是先进后出的容器,所以需要将左右调换次序。

前序是 中左右,如果将循环内的压栈顺序该一下就可以得到 中右左,然后再将数组进行翻转就可以得到 左右中,也就是后序遍历的次序。

后序遍历代码如下:

class Solution {
public:vector<int> postorderTraversal(TreeNode* root) {// 使用栈容器,模拟递归调用的函数栈vector<int> result;if (root == nullptr) return result;stack<TreeNode*> node;node.push(root);while(!node.empty()){// 栈顶数据存下来,然后弹出栈result.push_back(node.top()->val);TreeNode* temp = node.top();node.pop();// 然后判断刚才弹出的栈顶元素的左子树是否为空,若不为空,则压栈if(temp->left != nullptr){node.push(temp->left);}// 然后判断刚才弹出的栈顶元素的右子树是否为空,若不为空,则压栈if(temp->right != nullptr){node.push(temp->right);}} reverse(result.begin(), result.end());return result;}
};

2.2 中序

中序遍历的迭代不同通过前序的简单调换顺序来实现。

分析一下为什么刚刚写的前序遍历的代码,不能和中序遍历通用呢,因为前序遍历的顺序是中左右,先访问的元素是中间节点,要处理的元素也是中间节点,所以刚刚才能写出相对简洁的代码,因为要访问的元素和要处理的元素顺序是一致的,都是中间节点。

那么再看看中序遍历,中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(也就是在把节点的数值放进result数组中),这就造成了处理顺序和访问顺序是不一致的。

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

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

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

相关文章

【教程】APP加固的那些小事情

摘要 APP加固是保护APP代码逻辑的重要手段&#xff0c;通过隐藏、混淆、加密等操作提高软件的逆向成本&#xff0c;降低被破解的几率&#xff0c;保障开发者和用户利益。本文将介绍APP加固常见失败原因及解决方法&#xff0c;以及处理安装出现问题的情况和资源文件加固策略选择…

html--蝴蝶

<!DOCTYPE html> <html lang"en" > <head> <meta charset"UTF-8"> <title>蝴蝶飞舞</title> <link rel"stylesheet" href"https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.cs…

关于Transfomer的思考

为何诞生 在说transformer是什么&#xff0c;有什么优势之类的之前&#xff0c;先谈一谈它因何而诞生。transformer诞生最重要的原因是早先的语言模型&#xff0c;比如RNN&#xff0c;由于其本身的训练机制导致其并行度不高&#xff0c;特别是遇到一些长句子的情况下。其次&…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:ListItem)

用来展示列表具体item&#xff0c;必须配合List来使用。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。该组件的父组件只能是List或者ListItemGroup。 子组件 可以包含单个子组件。 接口 从API…

学习Python,需要知道的经典案例

文章目录 一、Python简介二、Python经典案例1. 猜数字游戏2. 文本文件处理3. 网络爬虫4. 数据可视化5. 电子邮件发送6. 实现一个简单的Web服务器。 三、Python处理IP相关知识点1. 处理IP地址2. 网络编程&#xff08;TCP/IP&#xff09;3. 使用第三方库处理IP信息 四、相关链接 …

Mysql与MyBatis

1 Sql语句 增删改查 1.1 建表 -- cmd展示数据库 show databases ; -- cmd登录数据库 mysql localhost -u root -p-- auto_increment 自动增长&#xff0c;每添加一个表项id自动增1 -- char定长字符串 0-255&#xff0c;不足十个字符按十个字符算&#xff0c; varchar变长字符串…

搭建项目后台系统基础架构

任务描述 1、了解搭建民航后端框架 2、使用IDEA创建基于SpringBoot、MyBatis、MySQL、Redis的Java项目 3、以原项目为参照搭建项目所涉及到的各个业务和底层服务 4、以原项目为例&#xff0c;具体介绍各个目录情况并参照创建相关文件夹 1、创建项目后端 BigData-KongGuan …

Transformer的前世今生 day01(预训练、统计语言模型)

预训练 在相似任务中&#xff0c;由于神经网络模型的浅层是通用的&#xff0c;如下图&#xff1a; 所以当我们的数据集不够大&#xff0c;不能产生性能良好的模型时&#xff0c;可以尝试让模型B在用模型A的浅层基础上&#xff0c;深层的部分自己生成参数&#xff0c;减小数据集…

tp8 mpdf 导出pdf

1. 安装mpdf composer require mpdf/mpdf 2. 然后 使用 use mpdf\Mpdf; 或者 require_once __DIR__ . /vendor/autoload.php; 官方文档 mPDF – mPDF 手册 文档里有很多东西 可以自己去研究 3. 编写代码 下载 (支持中文) $mpdf new Mpdf([mode > utf-8,"autoS…

Netty学习——源码篇2 客户端Bootstrap(二)

接上篇 Bootstrap源码-客户端 1 Handler的添加过程 Netty有一个强大和灵活之处就是基于Pipeline的自定义Handler机制。基于此&#xff0c;可以像添加插件一样自由组合各种各样的Handler来完成业务逻辑。例如&#xff0c;需要处理HTTP数据&#xff0c;那么就可以在Pipeline前添…

基于java+springboot+vue实现的电影院选票系统(文末源码+Lw+ppt)23-467

摘 要 时代在飞速进步&#xff0c;每个行业都在努力发展现在先进技术&#xff0c;通过这些先进的技术来提高自己的水平和优势&#xff0c;电影院选票系统当然不能排除在外。电影院选票系统是在实际应用和软件工程的开发原理之上&#xff0c;运用java语言&#xff0c;前台Vue框…

Hive:数据仓库利器

1. 简介 Hive是一个基于Hadoop的开源数据仓库工具&#xff0c;可以用来存储、查询和分析大规模数据。Hive使用SQL-like的HiveQL语言来查询数据&#xff0c;并将其结果存储在Hadoop的文件系统中。 2. 基本概念 介绍 Hive 的核心概念&#xff0c;例如表、分区、桶、HQL 等。 …

k8s部署hadoop

&#xff08;作者&#xff1a;陈玓玏&#xff09; 配置和模板参考helm仓库&#xff1a;https://artifacthub.io/packages/helm/apache-hadoop-helm/hadoop 先通过以下命令生成yaml文件&#xff1a; helm template hadoop pfisterer-hadoop/hadoop > hadoop.yaml用kube…

NodeJs利用腾讯云实现手机发送验证码

本文介绍如何在nodejs实现短信发送&#xff0c;以腾讯云的短信验证为例。 腾讯云中准备工作 首先需要腾讯云的个人或者企业认证的账号&#xff0c;个人会赠送一百条&#xff0c;企业赠送一千条&#xff0c;可以用于测试&#xff0c;地址&#xff1a;腾讯云短信服务。然后需要…

电机学(笔记一)

磁极对数p&#xff1a; 直流电机的磁极对数是指电机定子的磁极对数&#xff0c;也等于电机电刷的对数。它与电机的转速和扭矩有直接关系。一般来说&#xff0c;极对数越多&#xff0c;电机转速越低&#xff0c;扭矩越大&#xff0c;适用于低速、高扭矩的场合&#xff1b;相反&…

免 费 搭 建 多模式商城:b2b2c、o2o、直播带货一网打尽

鸿鹄云商 b2b2c产品概述 【b2b2c平台】&#xff0c;以传统电商行业为基石&#xff0c;鸿鹄云商支持“商家入驻平台自营”多运营模式&#xff0c;积极打造“全新市场&#xff0c;全新 模式”企业级b2b2c电商平台&#xff0c;致力干助力各行/互联网创业腾飞并获取更多的收益。从消…

IonQ最新研究突破!引入光量子纠缠以构建量子计算网络

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 编辑丨慕一 编译/排版丨沛贤 深度好文&#xff1a;700字丨5分钟阅读 2024年2月22日&#xff0c;美国量子计算公司IonQ宣布&#xff0c;公司研究团队已实现可重复地生成与离子纠缠的光子&#…

Python之Web开发中级教程----Django站点管理

Python之Web开发中级教程----Django站点管理 网站的开发分为两部分&#xff1a;内容发布和公共访问 内容发布是由网站的管理员负责查看、添加、修改、删除数据 Django能够根据定义的模型类自动地生成管理模块 使用Django的管理模块, 需要按照如下步骤操作 : 1.管理界面本地…

图论题目集一(代码 注解)

目录 题目一&#xff1a; 题目二&#xff1a; 题目三&#xff1a; 题目四&#xff1a; 题目五&#xff1a; 题目六&#xff1a; 题目七&#xff1a; 题目一&#xff1a; #include<iostream> #include<queue> #include<cstring> using namespace st…

rviz上不显示机器人模型(模型只有白色)

文档中的是base_footprint&#xff0c;需要根据自己所设的坐标系更改&#xff0c;我的改为base_link 如何查看自己设的坐标系&#xff1a; 这些parent父坐标系就是 同时打开rviz后需要更改成base_link