力扣日记12.13-【二叉树篇】从中序与后序遍历序列构造二叉树

力扣日记:【二叉树篇】从中序与后序遍历序列构造二叉树

日期:2023.12.13
参考:代码随想录、力扣

106. 从中序与后序遍历序列构造二叉树

题目描述

难度:中等

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

示例 1:
在这里插入图片描述

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

示例 2:

输入:inorder = [-1], postorder = [-1]
输出:[-1]

提示:

  • 1 <= inorder.length <= 3000
  • postorder.length == inorder.length
  • -3000 <= inorder[i], postorder[i] <= 3000
  • inorder 和 postorder 都由 不同 的值组成
  • postorder 中每一个值都在 inorder 中
  • inorder 保证是树的中序遍历
  • postorder 保证是树的后序遍历

题解

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
# define SOLUTION 2
public:
# if SOLUTION == 1TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {// 递归返回值与参数:返回值为中节点, 参数为中序和后序数组// 从中序与后序遍历序列构造二叉树的步骤// 1. 如果后序数组为空, 则为空节点(终止条件)if (postorder.size() == 0) return nullptr;  // 为空节点// 2. 根据后序数组最后一个值得到中节点值int nodeVal = postorder[postorder.size() - 1];  // 后序数组最后一个值为中节点值TreeNode* node = new TreeNode(nodeVal); // 构造中节点// 注意如果是叶子节点, 则不需要再去切割, 直接返回当前节点if (postorder.size() == 1) return node;// 3. 寻找中序数组位置作切割点int index = 0;for (int i = 0; i < inorder.size(); i++) {if (inorder[i] == nodeVal) {index = i;  // index 为中节点值对应序号, 则[0, index)为左子树, [index + 1, size)为右子树, 注意统一区间开闭 }}// 4. 根据此切割点对中序数组进行切割vector<int> inorderLeft(inorder.begin(), inorder.begin() + index);  // [0, index)vector<int> inorderRight(inorder.begin() + index + 1, inorder.end());   // [index + 1, size)// 5. 再根据切割后中序数组左右区间长度对后序数组进行切割vector<int> postorderLeft(postorder.begin(), postorder.begin() + inorderLeft.size()); // [0, left.size)vector<int> postorderRight(postorder.begin() + inorderLeft.size(), postorder.begin() + inorderLeft.size() + inorderRight.size()); // [left.size, left.size + right.size)// 6. 将中序数组与后序数组的左子树区间进行递归处理, 递归返回值为左子树的根节点,作为当前node左节点node->left = buildTree(inorderLeft, postorderLeft);   // 中序和后序的左子树遍历数组都分别按中序和后序遍历// 7. 将中序数组与后序数组的右子树区间进行递归处理node->right = buildTree(inorderRight, postorderRight);return node;    // 返回已经接上左右节点的中节点}
# elif SOLUTION == 2    // 优化:用下标索引,不需要每次构建子数组TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {int inorderBegin = 0, inorderEnd = inorder.size();  // [0, size)int postorderBegin = 0, postorderEnd = postorder.size();    // [0, size)return traversal(inorder, inorderBegin, inorderEnd, postorder, postorderBegin, postorderEnd);}TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postorderBegin, int postorderEnd) {// 递归返回值与参数:返回值为中节点, 参数为原始中序后序数组, 以及用来表示当前中后序数组的下标// 不变量:左闭右开// 从中序与后序遍历序列构造二叉树的步骤// 1. 如果当前后序数组为空, 则为空节点(终止条件)if (postorderEnd - postorderBegin == 0) return nullptr;  // 为空节点 [Begin, Begin)// 2. 根据后序数组最后一个值得到中节点值int nodeVal = postorder[postorderEnd - 1];  // 后序数组最后一个值为中节点值, 右开, 则需-1TreeNode* node = new TreeNode(nodeVal); // 构造中节点// 注意如果是叶子节点, 则不需要再去切割, 直接返回当前节点if (postorderEnd - postorderBegin == 1) return node;// 3. 寻找中序数组位置作切割点int index = 0;for (int i = inorderBegin; i < inorderEnd; i++) {if (inorder[i] == nodeVal) {index = i;  // index 为中节点值对应序号, 则[inorderBegin, index)为左子树, [index + 1, inorderEnd)为右子树, 注意统一区间开闭 }}// 4. 根据此切割点对中序数组进行切割int inorderLeftBegin = inorderBegin; // 左子树区间的开始下标(左闭)int inorderLeftEnd = index; // 左子树区间的结束下标(右开)int inorderRightBegin = index + 1;  // 右子树区间int inorderRightEnd = inorderEnd;// 5. 再根据切割后中序数组左右区间长度对后序数组进行切割int LeftSize = inorderLeftEnd - inorderLeftBegin;    // 左子树大小int postorderLeftBegin = postorderBegin;int postorderLeftEnd = postorderBegin + LeftSize;    // 后序与中序的左子树数组大小一致, LeftSize: [postorderLeftBegin, postorderLeftBegin + LeftSize)int RightSize = inorderRightEnd - inorderRightBegin;int postorderRightBegin = postorderLeftEnd; // 左闭int postorderRightEnd = postorderLeftEnd + RightSize;// 6. 将中序数组与后序数组的左子树区间进行递归处理, 递归返回值为左子树的根节点,作为当前node左节点node->left = traversal(inorder, inorderLeftBegin, inorderLeftEnd, postorder, postorderLeftBegin, postorderLeftEnd);   // 中序和后序的左子树遍历数组都分别按中序和后序遍历// 7. 将中序数组与后序数组的右子树区间进行递归处理node->right = traversal(inorder, inorderRightBegin, inorderRightEnd, postorder, postorderRightBegin, postorderRightEnd);return node;    // 返回已经接上左右节点的中节点}
# endif
};

复杂度

时间复杂度:
空间复杂度:

思路总结

  • 从中序与后序遍历序列构造二叉树的步骤
      1. 如果后序数组为空, 则为空节点
      2. 根据后序数组最后一个值得到中节点值
      3. 寻找中序数组位置作切割点
      4. 根据此切割点对中序数组进行切割
      5. 再根据切割后中序数组左右区间长度对后序数组进行切割
      6. 将中序数组与后序数组的左子树区间进行递归处理(两个左子树区间数组分别为左子树的中序数组和后序数组)
      7. 将中序数组与后序数组的右子树区间进行递归处理
  • 示意图
    在这里插入图片描述
  • 对于第二种解法,即用下标索引表示子数组(而不是直接构造子数组),要分别确定好左右子树的中序和后序数组的开始、结束下标。统一用左闭右开来表示。相对繁琐一些,但时间和空间复杂度更优化。

相关题目:105. 从前序与中序遍历序列构造二叉树

题目描述

难度:中等

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例 1:
在这里插入图片描述

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

示例 2:

输入: preorder = [-1], inorder = [-1]
输出: [-1]

提示:

  • 1 <= preorder.length <= 3000
  • inorder.length == preorder.length
  • -3000 <= preorder[i], inorder[i] <= 3000
  • preorder 和 inorder 均 无重复 元素
  • inorder 均出现在 preorder
  • preorder 保证 为二叉树的前序遍历序列
  • inorder 保证 为二叉树的中序遍历序列

题解

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {// 1. 如果前序数组为空, 则为空节点if (preorder.size() == 0)   return nullptr;// 2. 根据前序数组第一个值得到中节点值int nodeVal = preorder[0];// 构造中节点TreeNode* node = new TreeNode(nodeVal);// 如果是叶子节点直接返回if (preorder.size() == 1)   return node;// 3. 寻找中序数组位置作切割点int index = 0;for (int i = 0; i < inorder.size(); i++) {if (inorder[i] == nodeVal) {index = i;}}// 4. 根据此切割点对中序数组进行切割vector<int> inorderLeft(inorder.begin(), inorder.begin() + index);vector<int> inorderRight(inorder.begin() + index + 1, inorder.end());// 5. 再根据切割后的中序数组左右区间长度对前序数组进行切割vector<int> preorderLeft(preorder.begin() + 1, preorder.begin() + 1 + inorderLeft.size());  // 第二个值开始vector<int> preorderRight(preorder.begin() + 1 + inorderLeft.size(), preorder.end());// 6. 将中序数组与前序数组的左子树区间进行递归处理(两个左子树区间数组分别为左子树的中序数组和前序数组)node->left = buildTree(preorderLeft, inorderLeft);// 7. 将中序数组与前序数组的右子树区间进行递归处理node->right = buildTree(preorderRight, inorderRight);return node;}
};

复杂度

思路总结

  • 与 从后序与中序遍历序列构造二叉树 的思路基本一致
  • 从前序与中序遍历序列构造二叉树的步骤(前序:中左右;中序:左中右)
      1. 如果前序数组为空, 则为空节点
      2. 根据前序数组第一个值得到中节点值
      3. 寻找中序数组位置作切割点
      4. 根据此切割点对中序数组进行切割
      5. 再根据切割后的中序数组左右区间长度对前序数组进行切割
      6. 将中序数组与前序数组的左子树区间进行递归处理(两个左子树区间数组分别为左子树的中序数组和前序数组)
      7. 将中序数组与前序数组的右子树区间进行递归处理
  • 这里要明确一点:前序和中序、后序和中序都可以分别唯一确定一棵二叉树;但前序和后序不能唯一确定一棵二叉树!因为没有中序遍历无法确定左右部分,也就是无法分割。
    • 在这里插入图片描述
    • 上面两棵树的前序、后序都分别相等

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

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

相关文章

基于vue实现的疫情数据可视化分析及预测系统-计算机毕业设计推荐 django

本疫情数据可视化分析及预测系统 开发&#xff0c;用小巧灵活的MySQL数据库做完后台存储解释。本系统不仅主要实现了注册登录&#xff0c;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;全国实时数据管理&#xff0c;每日实时数据管理&#xff0c;国内实时动态…

@Scheduled任务调度/定时任务-非分布式

1、功能概述 任务调度就是在规定的时间内执行的任务或者按照固定的频率执行的任务。是非常常见的功能之一。常见的有JDK原生的Timer, ScheduledThreadPoolExecutor以及springboot提供的Schduled。分布式调度框架如QuartZ、Elasticjob、XXL-JOB、SchedulerX、PowerJob等。 本文…

SpringBoot进行自然语言处理,利用Hanlp进行文本情感分析

. # &#x1f4d1;前言 本文主要是SpringBoot进行自然语言处理&#xff0c;利用Hanlp进行文本情感分析&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页放风…

医保电子凭证在项目中的集成应用

随着医保电子凭证使用普及&#xff0c;医疗行业的各个场景都要求支持医保码一码通办&#xff0c;在此分享一下&#xff0c;在C#和js中集成医保电子凭证的demo 供有需要的小伙伴参考。 一、项目效果图 在c#中集成医保电子凭证效果 在js中集成医保电子凭证效果 二、主要代码 c#…

EasyRecovery2024功能强大且专业的mac电脑数据恢复程序

EasyRecovery15是一款功能强大且专业的IOS数据恢复程序&#xff0c;专为在iPhone&#xff0c;iPad和iPod touch上检索丢失的照片&#xff0c;消息&#xff0c;音乐等而设计。无论您是错误删除还是意外丢失了对您来说重要的任何内容&#xff0c;EasyRecovery都会帮助您找回它们。…

群晖(Synology)新建存储池使用 Home 服务

每一个用户都可以有一个自己的 Home 服务。 这个在群晖存储新建存储池后可以自动启用这个服务。 启用后&#xff0c;可以看到你的文件系统中有一个 homes 的文件了。 群晖&#xff08;Synology&#xff09;新建存储池使用 Home 服务 - 系统容器 - iSharkFly每一个用户都可以有…

flutter调试器查看不了副页面(非主页面/子页面)

刚接触flutter&#xff0c;写了两个页面&#xff0c;通过按钮&#xff0c;可以从主页面跳转到副页面&#xff0c;副页面我自己写的一个独立的dart文件&#xff0c;在主页面的代码中导入使用。但是当我运行代码后&#xff0c;点击跳转的时候&#xff0c;却发现查看不到对应的副页…

nodejs微信小程序+python+PHP沧州地区空气质量数据分析系统-计算机毕业设计推荐django

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

linux(centos7)离线安装mysql-5.7.35-1.el7.x86_64.rpm-bundle.tar

1. 卸载mariadb相关rpm # 查找 rpm -qa|grep mariadb rpm -qa|grep mysql# 卸载 rpm -e --nodeps mariadb... rpm -e --nodeps mysql...2. 删除mysql相关文件 # 查找 find / -name mysql# 删除 rm -rf /var/lib/mysql...3. 查看是否有相关依赖&#xff0c;没有需安装 rpm -q…

Android Studio的代码笔记--Adapter+GridView学习

AdapterGridView学习 AdapterGridViewSimpleAdapterGridViewactivity_main.xmlappicon.xmlMainActivity 自定义BaseAdapterGridView已下载应用PackageInfoAppAdapterMainActivity2 其他获取已下载应用信息函数获取所有应用信息函数ImageView产生圆角的方法背景设置很渐变设置选…

Hadoop学习总结(Hive的安装)

Hive的安装模式分为3种&#xff0c;分别是嵌入模式、本地模式、远程模式。 &#xff08;1&#xff09;嵌入模式&#xff1a;使用内嵌的 Derby 数据库存储元数据&#xff0c;这种方式是 Hive 的默认安装方式&#xff0c;配置简单&#xff0c;但是一次只能连接一个客户端&#xf…

模块一——双指针:18.四数之和

文章目录 题目描述算法原理排序双指针 代码实现排序双指针复杂度分析时间复杂度&#xff1a;O(N^3^)空间复杂度&#xff1a;O(log⁡N)或者O(N) 题目描述 题目链接&#xff1a;18.四数之和 算法原理 排序双指针 依次固定⼀个数a &#xff1b;在这个数a 的后⾯区间上&#x…

C#实现支付宝转账功能

环境 .net 6 AlipaySDKNet.OpenAPI 2.4.0 申请证书 登录支付宝开放平台https://open.alipay.com/ 进入控制台 授权回调地址也设置一下&#xff0c;加密方式AES 新建.net 6空白的web项目 证书除了java都需要自己生成一下pkcs1的密钥 privatekey.txt就是根据应用私钥生成…

玩转大数据16:大数据存储与文件格式优化

随着大数据时代的到来&#xff0c;存储和处理海量数据成为了一个重要的挑战。在大数据存储中&#xff0c;选择合适的文件格式对数据的压缩率、读写性能和扩展性起着关键作用。本文将介绍大数据存储的挑战&#xff0c;探讨常见的文件格式&#xff0c;并深入讨论文件格式优化的策…

小新Air-14 Plus 2021款AMD ACN版(82L7)原装出厂Win11系统镜像

LENOVO联想笔记本开箱状态原厂Windows11系统包 链接&#xff1a;https://pan.baidu.com/s/1D_sYCJAtOeUu9RbTIXgI3A?pwd96af 提取码&#xff1a;96af 联想小新AIR14笔记本电脑原厂系统自带所有驱动、出厂主题壁纸、Office办公软件、联想电脑管家等预装程序 所需要工具&am…

Mapreduce小试牛刀(1)

1.与hdfs一样&#xff0c;mapreduce基于hadoop框架&#xff0c;所以我们首先要启动hadoop服务器 --------------------------------------------------------------------------------------------------------------------------------- 2.修改hadoop-env.sh位置JAVA_HOME配…

MBR30300FCT-ASEMI高耐压肖特基MBR30300FCT

编辑&#xff1a;ll MBR30300FCT-ASEMI高耐压肖特基MBR30300FCT 型号&#xff1a;MBR30200FCT 品牌&#xff1a;ASEMI 封装&#xff1a;TO-220F 最大平均正向电流&#xff1a;30A 最大重复峰值反向电压&#xff1a;300V 产品引线数量&#xff1a;3 产品内部芯片个数&…

***Cpolar配置外网访问和Dashy

Dashy是一个开源的自托管的导航页配置服务,具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。你可以将自己常用的一些网站聚合起来放在一起,形成自己的导航页。一款功能超强大,颜值爆表的可定制专属导航页工具 结合cpolar内网工具,我们实现无需部署到公网服务器…

一文1000字基于Jenkins实现接口自动化持续集成!

一、JOB项目配置 1、添加描述 可选选项可填可不填 2、限制项目的运行节点 节点中要有运行环境所需的配置 节点配置教程&#xff1a;https://blog.csdn.net/YZL40514131/article/details/131504280 3、源码管理 需要将脚本推送到远程仓库中 4、构建触发器 可以选择定时构建…

vite 打包图标icon ,content 内容乱码

问题描述&#xff1a;本地开发环境icon 显示正常&#xff0c;打包后发布线上环境icon乱码&#xff0c;而且具有偶发性&#xff0c;刷新页面乱码又正常了。 找问题&#xff1a;观察发现是content 内容没有编译成功导致乱码 解决&#xff1a;vite.config.ts / vite.config.js 文件…