算法刷题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数组中),这就造成了处理顺序和访问顺序是不一致的。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[Linux_IMX6ULL应用开发]-Makefile

目录 Makefile的规则 Makefile的语法 通配符 假想目标 变量 Makefile的函数 foreach函数 filter和filter-out wildcard patsubst 修改头文件无法make解决 CFLAGS Makefile的规则 当我们在使用gcc进行编译链接的时候&#xff0c;我们需要手动在shell窗口键入命令。比…

大模型“说胡话”现象辨析

在人工智能快速发展的今天&#xff0c;大型深度学习模型已成为自然语言处理领域的核心力量。然而&#xff0c;随着这些模型规模的不断扩大和功能的日益增强&#xff0c;一种被称为“说胡话”的现象也愈发引人关注。这种现象不仅影响了模型在实际应用中的效果&#xff0c;也引发…

Linux入门-常见指令及权限理解

目录 1、Linux背景 1.1、发展历史 1.2、开源 1.3Linux企业应用现状 2、Linux下的基本命令 2.1、ls 指令 2.2、pwd 命令 2.3、cd 命令 2.4、touch命令 2.5、mkdir 命令 2.6、rmdir 指令和 rm指令 2.7 man 指令 2.8、cp指令 2.9、mv 指令 2.10 cat 2.11 more 2…

RocketMq 顺序消费、分区消息、延迟发送消息、Topic、tag分类 实战 (消费者) (三)

消费端配置 如下所示&#xff1a;是消费者的配置类&#xff0c;有以下几点需要注意的地方 1、是TargetMessageListener这个监听类&#xff08;下文会把这个监听类的具体代码贴出来&#xff09;&#xff0c;需要把这个监听类订阅。 2、rocketMqDcProperties.getTargetProperties…

MySQL 多表查询与事务的操作

一,多表联查 有些数据我们已经拆分成多个表,他们之间通过外键进行连接.当我们要查询两个表的数据,各取其中的一列或者多列. 这时候就需要使用多表联查. 数据准备: # 创建部门表 create table dept(id int primary key auto_increment,name varchar(20) ) insert into dept (n…

力扣---打家劫舍---动态规划

思路 1&#xff1a; 我将res[i]定义为&#xff1a;一定要取第 i 个房子的前提下&#xff0c;能获取的最大金额。那么直接用cnt从头记录到尾&#xff0c;每个房子的res最大值即是答案。那么递推公式是什么&#xff1f;res[i]max(res[i-2],res[i-1],...,res[0])nums[i]。数组初始…

cmake与交叉编译(x86 to arm)过程和问题全记录

一、背景 公司维护一批c动态库&#xff0c;由于生产需要&#xff0c;每次更新都要在windows、linux_x86、kylin_arm等多个环境中编译一遍&#xff0c;操作比较麻烦&#xff0c;所以想通过交叉编译的方式在一台机器上边编译多个环境的动态库&#xff0c;减少工作量。考虑到工作…

浅谈大模型“幻觉”问题

大模型的幻觉大概来源于算法对于数据处理的混乱&#xff0c;它不像人类一样可以by the book&#xff0c;它没有一个权威的对照数据源。 什么是大模型幻觉 大模型的幻觉&#xff08;Hallucination&#xff09;是指当人工智能模型生成的内容与提供的源内容不符或没有意义的现象。…

Linux——程序地址空间

我们先来看这样一段代码&#xff1a; #include <stdio.h> #include <unistd.h> #include <stdlib.h>int g_val 0;int main() {pid_t id fork();if(id < 0){perror("fork");return 0;}else if(id 0){ //child,子进程肯定先跑完&#xff0c;也…

提升Java编程安全性-代码加密混淆工具的重要性和应用

在Java编程领域中&#xff0c;保护代码安全性和知识产权至关重要。本文旨在探讨代码加密混淆工具在提升代码安全性和保护知识产权方面的重要性。我们将介绍几款流行的Java代码加密混淆工具&#xff0c;如ProGuard、DexGuard、Jscrambler、DashO和ipaguard&#xff0c;并分析它们…

多线程(剩余部分)

Day29 多线程(剩余部分) 十二、线程的礼让 Thread.yield(); 理解&#xff1a;此方法为静态方法&#xff0c;此方法写在哪个线程中&#xff0c;哪个线程就礼让 注意&#xff1a;所谓的礼让是指当前线程退出CPU资源&#xff0c;并转到就绪状态&#xff0c;接着再抢 需求&#x…

浅谈一下对于DDD模式的理解3

浅谈一下对于DDD模式的理解&#xff0c;相互学习交流&#xff0c;不对之处欢迎大家指正。 在说到DDD(Domain-Driven Design)设计模式之前&#xff0c;先要说下我们在对系统进行架构设时需要遵循的几个原则&#xff1a; 单一职责&#xff08;SRP&#xff09; "单一职责原则…

直播预约丨《袋鼠云大数据实操指南》No.1:从理论到实践,离线开发全流程解析

近年来&#xff0c;新质生产力、数据要素及数据资产入表等新兴概念犹如一股强劲的浪潮&#xff0c;持续冲击并革新着企业数字化转型的观念视野&#xff0c;昭示着一个以数据为核心驱动力的新时代正稳步启幕。 面对这些引领经济转型的新兴概念&#xff0c;为了更好地服务于客户…

文献速递:基于SAM的医学图像分割---阶梯式微调方法,用于整合补充网络的自适应矩估计(SAM)

Title 题目 Ladder Fine-tuning approach for SAM integrating complementary network 阶梯式微调方法&#xff0c;用于整合补充网络的自适应矩估计&#xff08;SAM&#xff09; 01 文献速递介绍 医学图像分割在医疗保健中扮演着至关重要的角色。它旨在使用各种医学成像方式…

MS2574/2574T/2574S高速、四通道差动线路驱动器

品简述 MS2574/MS2574T/MS2574S 是一款高速、低功耗的四通道 差动线路驱动芯片&#xff0c;用于平衡或非平衡的数字数据传输。可 以满足 ANSI TIA/EIA-422-B 和 ITU &#xff08;原 CCITT &#xff09;建议 V.11 的要求。 三态输出可提供用于驱动双绞线或平行双线传输线路等…

公司购买阿里云服务器多少钱一年?199元2核4G5M配置

阿里云服务器ECS u1实例&#xff0c;2核4G&#xff0c;5M固定带宽&#xff0c;80G ESSD Entry盘优惠价格199元一年&#xff0c;性能很不错&#xff0c;CPU采用Intel Xeon Platinum可扩展处理器&#xff0c;购买限制条件为企业客户专享&#xff0c;实名认证信息是企业用户即可&a…

基于机器视觉的太阳能电池片异物遮挡检测含数据集

分享链接见文末 近年来&#xff0c;随着太阳能发电技术的快速发展&#xff0c;太阳能电池片的应用越来越广泛。然而&#xff0c;太阳能电池片在实际运行过程中常常会受到各种异物的遮挡&#xff0c;如树叶、灰尘等&#xff0c;导致发电效率下降甚至损坏设备。因此&#xff0c;…

python 基于 websocket 的简单将视频推流到网页

本来有一台设备是要搞成无线的形式的&#xff0c;设备的摄像头的数据可以在一台局域网连接的平板上查看&#xff0c;因为试着使用 RTMP 推流&#xff0c;感觉延时太大了&#xff0c;而 Webrtc 感觉有太麻烦了&#xff0c;所以一开始看到这篇文章使用 UDP 协议进行推流&#xff…

竞赛 - 基于机器视觉的图像拼接算法

前言 图像拼接在实际的应用场景很广&#xff0c;比如无人机航拍&#xff0c;遥感图像等等&#xff0c;图像拼接是进一步做图像理解基础步骤&#xff0c;拼接效果的好坏直接影响接下来的工作&#xff0c;所以一个好的图像拼接算法非常重要。 再举一个身边的例子吧&#xff0c;…

“比特币跌至8900美元”?逢低买入信号闪现!亚洲投资者需求正持续增长!

3月19日&#xff0c;美股三大指数集体收涨&#xff0c;美联储正在召开为期两天的货币政策会议&#xff0c;周三公布结果&#xff0c;市场普遍预计美联储将按兵不动。 然而&#xff0c;比特币近几日却面临显著的价格回调&#xff0c;昨早再次从6.7万美元水平快速下滑&#xff0c…