力扣145题:二叉树的后序遍历

给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 

示例 1:

输入:root = [1,null,2,3]
输出:[3,2,1]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

提示:

  • 树中节点的数目在范围 [0, 100] 内
  • -100 <= Node.val <= 100

【方法一】递归算法

二叉树的后序遍历按照:左-右-根来进行。

针对主函数,我们需要定义一个数组a,将遍历的树中元素放在数组中;returnSize指针所指向的值初始化为0,表示结果数组的大小初始为0;调用postorder函数,返回数组a。

针对调用函数,首先进行判断是否为空,为空的话直接进行返回,不需要进行任何操作。然后按照左-右-根来进行遍历。针对根节点处理:a[(*returnSize)++]=root->val。

具体案例分析

root = [1,null,2,3],输出:[3,2,1]。

针对主函数,开始时a[0]={},returnSize=0,调用postorder(root->left, a, returnSize),为空直接返回。进行调用postorder(root->right, a, returnSize)。由于root=2不为空,则继续进行调用postorder(root->left, a, returnSize)和postorder(root->right, a, returnSize)。

针对调用postorder(root->left, a, returnSize),此时root=3,进行调用,发现左和右均为空,则将3添加到数组a[0]中,并将 *returnSize 递增为1。然后返回到根为2时进行调用postorder(root->right, a, returnSize),为空,则将2添加到数组a[1]中,并将 *returnSize 递增为1。

此时到达最外层循环,也就是roo=1时,左为空,右已经调用完,则将1添加到数组a[2]中,并将 *returnSize 递增为1。

最终,数组 a 包含元素 [1, 2, 3],并且 *returnSize 为3。

返回数组 a。

(整个流程可以描述为,root=1->postorder(root->left, a, returnSize)为空->调用postorder(root->right, a, returnSize),此时root=2->调用postorder(root->left, a, returnSize)和postorder(root->right, a, returnSize)->首先针对postorder(root->left, a, returnSize),root=3,左右均空,将root=3加入到数组中->然后针对postorder(root->right, a, returnSize),root=2->right为空,则直接将2加入数组中->返回到最外层循环,将1加入到数组中->返回a[1,2,3]。

C语言具体代码如下:

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
/*** Note: The returned array must be malloced, assume caller calls free().*/void postorder(struct TreeNode* root, int* a, int* returnSize) {if (!root) {  //树是空return;}postorder(root->left, a, returnSize);postorder(root->right, a, returnSize);a[(*returnSize)++] = root->val;  
}int* postorderTraversal(struct TreeNode* root, int* returnSize) {int* a = malloc(sizeof(int) * 100);*returnSize = 0; postorder(root, a, returnSize);return a;  
}

时间复杂度O(n);空间复杂度O(n)

【方法一】迭代算法

二叉树的后序遍历按照:左-右-根来进行。

后序遍历中,要保证左孩子和右孩子都已经被访问并且左孩子在右孩子前访问才能访问根节点。可通过辅助指针q来进行标记上一个访问的节点。

第一步创建一个栈。

第二步:定义一个stack来开辟一个栈空间,并将栈初始化,top=0。定义一个数组,用来返回最后后续遍历后的树,returnSize指针的值初始化为0,定义一个指针p来指向树的根,定义一个指针q指向空,用来标记上一个访问的节点。

第三步:当p不为空或者top不指在栈顶的时候,进判断。如果p不为空,stack->s[stack->top] = p,并将top++,进行移动,并将p指向左孩子。如果p为空的时候,p=stack->s[stack->top],然后进行判断p的右孩子是否为空或者为q。将栈中的元素给数组,并将q标记在p这个位置,然后将top--,并将p指向空。如果if语句不满足,直接将p指向右孩子。最后返回数组。

具体案例分析

root = [1,null,2,3],输出:[3,2,1]。

开始时:p=1,top=0,stack->s[0]=p=1,top++为1,将p指向左孩子为空。则,此时p=NULL。

p=NULL,执行else里面的语句。p=stack[1-1=0]=1,执行else 中if语句,不符合,则执行else中if中的else语句,p=p->right=2。则,此时p=2。

p=2,不为空,进行if (p != NULL)遇见,stack->s[1]=p=2(此时栈中1,2),top++为2,将p指向左孩子为3。则,p=3。

p=3,不为空,进行if (p != NULL)遇见,stack->s[2]=p=3(此时栈中1,2,3),top++为3,将p指向左孩子空。则,p=NULL。

p=NULL,执行else里面的语句。p=stack[3-1=2]=3,执行else 中if语句,符合,则a[0]={3},returnSize=1,q=3,top=2,p=NULL。

p=NULL,执行else里面的语句。p=stack[2-1=1]=2,执行else 中if语句,符合,则a[1]={3,2},returnSize=2,q=2,top=1,p=NULL。

p=NULL,执行else里面的语句。p=stack[1-1=0]=1,执行else 中if语句,符合,则a[2]={3,2,1},returnSize=3,q=1,top=0,p=NULL。

top=0,p=NULL,不符合while循环,跳出,返回a[2]={3,2,1}。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
/*** Note: The returned array must be malloced, assume caller calls free().*/
struct stack {struct TreeNode *s[100];int top;
};// 后序遍历函数
int* postorderTraversal(struct TreeNode* root, int* returnSize) {struct stack *stack = (struct stack *)malloc(sizeof(struct stack));stack->top = 0;int *a = (int *)malloc(sizeof(int) * 100);*returnSize = 0;struct TreeNode *p = root;struct TreeNode *q = NULL; // 用于记录上一次访问的节点while (p != NULL || stack->top != 0) {if (p != NULL) {stack->s[stack->top] = p;(stack->top)++;p = p->left;} else {p = stack->s[stack->top - 1];if (p->right == NULL || p->right == q) {a[(*returnSize)++] = p->val;q = p;(stack->top)--;p = NULL;} else {p = p->right;}}}return a;
}

时间复杂度O(n);空间复杂度O(n)

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

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

相关文章

C++复习的长文指南

C复习的长文指南 一、入门语法知识1.预备1.1 main函数1.2 注释1.3 变量1.3 常量1.4 关键字1.5 标识符明明规则 2. 数据类型2.1 整型2.1.1 sizeof关键字 2.2 实型&#xff08;浮点型&#xff09;2.3 字符型2.4 转义字符2.5 字符串型2.6 布尔类型bool2.7 数据的输入 3. 运算符3.1…

SwiftUI 6.0(Xcode 16)新 PreviewModifier 协议让预览调试如虎添翼

概览 用 SwiftUI 框架开发过应用的小伙伴们都知道&#xff0c;SwiftUI 中的视图由各种属性和绑定“扑朔迷离”的缠绕在一起&#xff0c;自成体系。 想要在 Xcode 预览中泰然处之的调试 SwiftUI 视图有时并不是件容易的事。其中&#xff0c;最让人秃头码农们头疼的恐怕就要数如…

对redis进行深入学习

目录 1. 什么是redis&#xff1f;1.1 为什么使用redis作为缓存&#xff1f;1.1.0 数据库&#xff08;MySQL&#xff09;与 redis1. 存储介质不同&#xff08;408选手应该都懂hh&#xff09;2. 数据结构优化3. I/O模型差异4. CPU缓存友好性5. 单线程与多线程差异6. 持久化与缓存…

进程的地址空间

一、写个代码见一见地址空间 1、问题 在代码中我们在第五秒时会在子进程中改变全局变量 g_val 但是我们发现了一个奇怪的现象&#xff1a;在子进程中改变 g_val &#xff0c;由于进程的独立性&#xff0c;所以子进程和父进程的值不一样是可以理解的&#xff0c;但是为什么变量…

【C++11】线程

本篇文章更多的是熟悉一下C11的线程库接口&#xff0c;与linux的相关线程接口是非常相似的&#xff0c;更多的是将面向过程改为了面向对象。 并没有一些概念的讲解。 想知道线程的相关概念的可以看一看这篇文章及后续 在C11之前&#xff0c;涉及到多线程问题&#xff0c;都是和…

访问控制系列

目录 一、基本概念 1.客体与主体 2.引用监控器与引用验证机制 3.安全策略与安全模型 4.安全内核 5.可信计算基 二、访问矩阵 三、访问控制策略 1.主体属性 2.客体属性 3.授权者组成 4.访问控制粒度 5.主体、客体状态 6.历史记录和上下文环境 7.数据内容 8.决策…

memcached 高性能内存对象缓存

memcached 高性能内存对象缓存 memcache是一款开源的高性能分布式内存对象缓存系统&#xff0c;常用于做大型动态web服务器的中间件缓存。 mamcached做web服务的中间缓存示意图 当web服务器接收到请求需要处理动态页面元素时&#xff0c;通常要去数据库调用数据&#xff0c;但…

【快速逆向一/无过程/有源码】《大学》在线投稿系统

逆向日期&#xff1a;2024.07.18 使用工具&#xff1a;Node.js 加密工具&#xff1a;Crypto-js标准库 文章全程已做去敏处理&#xff01;&#xff01;&#xff01; 【需要做的可联系我】 【点赞 收藏 关注 】仅供学习&#xff0c;仅供学习&#xff0c; 本文为快速逆向&#x…

如果制作红星照耀中国思维导图?6个软件帮助你快速制作思维导图

如果制作红星照耀中国思维导图&#xff1f;6个软件帮助你快速制作思维导图 制作《红星照耀中国》思维导图可以帮助更好地理解和梳理书中的重要信息和内容。以下是六款推荐的思维导图软件及其特点和使用方法&#xff0c;帮助你快速制作高质量的思维导图。 迅捷画图 特点与功…

Python基础语法篇(下)+ 数据可视化

Python基础语法&#xff08;下&#xff09; 数据可视化 一、函数&#xff08;一&#xff09;函数的定义&#xff08;二&#xff09;函数的调用和传参 二、文件操作&#xff08;一&#xff09;文件读取和写入&#xff08;二&#xff09;文件对象及方法&#xff08;三&#xff09…

【数学建模】——【线性规划】及其在资源优化中的应用

目录 线性规划问题的两类主要应用&#xff1a; 线性规划的数学模型的三要素&#xff1a; 线性规划的一般步骤&#xff1a; 例1&#xff1a; 人数选择 例2 &#xff1a;任务分配问题 例3: 饮食问题 线性规划模型 线性规划的模型一般可表示为 线性规划的模型标准型&…

达梦数据库的系统视图v$sqltext

达梦数据库的系统视图v$sqltext 在达梦数据库&#xff08;DM Database&#xff09;中&#xff0c;V$SQLTEXT 是一个系统视图&#xff0c;用于显示当前正在执行或最近执行的SQL语句的文本信息。这个视图对于监控和分析数据库中的SQL活动非常有用&#xff0c;尤其是在需要调试性…

【MySQL篇】Percona XtraBackup工具备份指南:常用备份命令详解与实践(第二篇,总共五篇)

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌️…

银河麒麟搭建ftp服务器

1.先 查看系统架构&#xff0c;我常遇到的一般银河麒麟是arrch64的 lscpu uname -a cat /etc/os-release 去下载对应版本的vsftp.rpm包和ftp包 Index of /NS/ (cs2c.com.cn) 1.安装rpm rpm -ivh *.rpm --nodeps --force #强制安装 2.修改配置文件 vi /etc/vsftpd/vsftpd.co…

PiT : 基于池化层Pooling layer的Vision Transformer

CNN的降维原理;随着深度的增加,传统CNN的通道维数增加,空间维数减少。经验表明,这样的空间降维对变压器结构也是有益的,并在原有的ViT模型的基础上提出了一种新的基于池的视觉变压器(PiT)。 1. 引言 ViT与卷积神经网络(CNN)有很大的不同。将输入图像分成1616小块馈送到变压…

LabVIEW软件开发的雷区在哪里?

在LabVIEW软件开发中&#xff0c;有几个需要注意的雷区&#xff0c;以避免常见的错误和提高开发效率&#xff1a; 1. 不良的代码结构 雷区&#xff1a;混乱的代码结构和不清晰的程序逻辑。 后果&#xff1a;导致难以维护和调试的代码&#xff0c;增加了错误和故障的风险。 …

Web3时代的教育技术革新:智能合约在学习管理中的应用

随着区块链技术的发展和普及&#xff0c;Web3时代正在为教育技术带来前所未有的革新和机遇。智能合约作为区块链技术的核心应用之一&#xff0c;不仅在金融和供应链管理等领域展示了其巨大的潜力&#xff0c;也在教育领域中逐渐探索和应用。本文将探讨智能合约在学习管理中的具…

【C++】前缀和:和为K的子数组

1.题目 2.算法 需要借助哈希表&#xff08;查找效率很高&#xff09;。 如果一个区间和为sum&#xff0c;如果它的前缀和为sum-k&#xff0c;那么后缀和一定是K。 3.代码

类与对象(3)

对于类的构造函数我们已经有了初步的了解&#xff0c;这里我们对其拷贝构造函数进行讲解&#xff1a; 目录 拷贝构造函数&#xff1a; 1.拷贝构造函数的作用&#xff1a; 2.系统生成拷贝构造函数的缺陷 3.深拷贝的实现 侧面体现 拷贝构造函数&#xff1a; 这里我们将拷贝…

小程序-模板与配置

一、WXML模板语法 1.数据绑定 2.事件绑定 什么是事件 小程序中常用的事件 事件对象的属性列表 target和currentTarget的区别 bindtap的语法格式 在事件处理函数中为data中的数据赋值 3.事件传参与数据同步 事件传参 &#xff08;以下为错误示例&#xff09; 以上两者的…