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

news2024/11/28 7:49:58

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

给定两个整数数组 inorderpostorder ,其中 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 ≤ i n o r d e r . l e n g t h ≤ 3000 1 \leq inorder.length \leq 3000 1inorder.length3000
  • postorder.length == inorder.length
  • − 3000 ≤ i n o r d e r [ i ] , p o s t o r d e r [ i ] ≤ 3000 -3000 \leq inorder[i], postorder[i] \leq 3000 3000inorder[i],postorder[i]3000
  • inorderpostorder 都由 不同 的值组成
  • postorder 中每一个值都在 inorder
  • inorder 保证是树的中序遍历
  • postorder 保证是树的后序遍历

解法一(递归+分治+Map哈希)

思路分析:

  1. 对于该题,首先思考;中序遍历为:左中右,后序遍历为:左右中,因此通过后序遍历可以确认二叉树的根节点,然后通过根节点可以对中序遍历进行切割成:左中序右中序;然后根据得到的左中序长度,可以对后序遍历进行切割成:左后序右后序
  2. 以此类推,通过递归分治的方式,可以从根节点建立一个二叉树。
  3. 同时思考递归的参数和返回值,因为题目要求构造一个二叉树,所以 返回值类型为TreeNode,然后对于递归的参数则包括,中序遍历数组、后序遍历数组、中序数组起始位置、中序数组末尾位置、后序数组起始位置、后序数组末尾位置。
  4. 对于递归的边界条件,则当后序遍历数组为null时,返回null,当由后序遍历索引起始及末尾位置得;数组长度为1时,直接返回
  5. 对于递归的过程,则是构造中间节点,以及递归构造左右节点
  6. 同时对于如何根据后序数组,对中序数组进行分割,可以使用Map哈希表的方式,避免对中序数组进行反复查询。

实现代码如下:

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
		if (postorder == null)
			return null;	// 边界条件
		// 构造哈希表
		Map<Integer, Integer> inMap = new HashMap<>();
		for (int i = 0; i < inorder.length; i++) {
			inMap.put(inorder[i], i);
		}
		return doBuildTree(inorder, postorder, inMap, 0, inorder.length-1, 0, postorder.length-1);
    }
	private TreeNode doBuildTree(int[] inorder, int[] postorder, Map<Integer, Integer> inMap, int inS, int inE, int postS, int postE) {
		if (inE < 0 || postE < 0 || inS > inE || postS > postE || inS >= inorder.length || postS >= postorder.length) // 考虑边界问题
			return null;
		// 根据后序遍历数组 末尾索引 获取该子树根节点值
		int rootValue = postorder[postE];
		TreeNode node = new TreeNode(rootValue);	// 构造二叉树
		if (postS == postE)		// 若此时后序数组 起始索引和末尾索引相等 说明为叶子节点
			return node;	// 直接返回
		// 根据根节点值 对中序数组进行分割 获取分割位置索引
		int index = inMap.get(rootValue);
		// 递归获取左右子树
		node.left = doBuildTree(inorder, postorder, inMap, inS, index-1, postS, postS+index-1-inS);
		node.right = doBuildTree(inorder, postorder, inMap, index+1, inE, postS+index-inS, postE-1);
		return node;
	}
}

提交结果如下:

解答成功:
执行耗时:2 ms,击败了62.35% 的Java用户
内存消耗:43.5 MB,击败了13.55% 的Java用户

复杂度分析:

  • 时间复杂度: O ( n + m ) O(n+m) O(n+m),需要遍历数组
  • 空间复杂度: O ( n + m ) O(n+m) O(n+m),考虑递归对空间的消耗

优化解法一

思路分析:

  1. 通过对解法一代码的执行流程,发现递归函数doBuildTree中的inorder参数可以省略
  2. 且对于doBuildTree函数中的边界问题判断,由于初始inEPostE均为len-1inSpostS初始为0,因此对于inE < 0的判断与inS >= inorder.length的判断包含在inS > inE中,可省略

实现代码如下:

class Solution {
	public TreeNode buildTree(int[] inorder, int[] postorder) {
		if (postorder == null)
			return null;	// 边界条件
		// 构造哈希表
		Map<Integer, Integer> inMap = new HashMap<>();
		for (int i = 0; i < inorder.length; i++) {
			inMap.put(inorder[i], i);
		}
		return doBuildTree(postorder, inMap, 0, inorder.length-1, 0, postorder.length-1);
	}
	private TreeNode doBuildTree(int[] postorder, Map<Integer, Integer> inMap, int inS, int inE, int postS, int postE) {
		if (inS > inE || postS > postE) // 考虑边界问题
			return null;
		// 根据后序遍历数组 末尾索引 获取该子树根节点值
		int rootValue = postorder[postE];
		TreeNode node = new TreeNode(rootValue);	// 构造二叉树
		if (postS == postE)		// 若此时后序数组 起始索引和末尾索引相等 说明为叶子节点
			return node;	// 直接返回
		// 根据根节点值 对中序数组进行分割 获取分割位置索引
		int index = inMap.get(rootValue);
		// 递归获取左右子树
		node.left = doBuildTree(postorder, inMap, inS, index-1, postS, postS+index-1-inS);
		node.right = doBuildTree(postorder, inMap, index+1, inE, postS+index-inS, postE-1);
		return node;
	}
}

提交结果如下:

解答成功:
执行耗时:2 ms,击败了62.35% 的Java用户
内存消耗:43.6 MB,击败了10.93% 的Java用户

复杂度分析:

  • 时间复杂度: O ( n + m ) O(n+m) O(n+m),遍历中序数组和后序数组
  • 空间复杂度: O ( n + m ) O(n+m) O(n+m),考虑每层递归传递参数对空间消耗。

解法二(递归+分治+Map)

思路分析:

  1. 跟据官方题解,将中序数组、后序数组,以及提交查询的Map变量,均改为全局遍历,即不需要作为递归函数参数,可在递归函数内访问。
  2. 因为后序遍历中,最后一个元素为子树的根节点,所以先递归获取右子树,再递归获取左子树

实现代码如下:

class Solution {
	int[] inorder;		// 中序遍历数组
	int[] postorder;	// 后序遍历数组
	Map<Integer, Integer> inMap;	// 中序遍历数组 索引表
	int postIndex;
	public TreeNode buildTree(int[] inorder, int[] postorder) {
		if (postorder == null)
			return null;	// 边界条件
		this.inorder = inorder;
		this.postorder = postorder;
		postIndex = postorder.length-1;
		// 构造哈希表
		inMap = new HashMap<>();
		for (int i = 0; i < inorder.length; i++) {
			inMap.put(inorder[i], i);
		}
		return doBuildTree(0, inorder.length-1);
	}
	private TreeNode doBuildTree(int inLeft, int inRight) {
		if (inLeft > inRight)	// 说明此时为空树
			return null;
		int value = postorder[postIndex];	// 根据postIndex 来确定当前子树 中节点值
		TreeNode node = new TreeNode(value);
		// 根据 中间节点值 获取分割中序数组索引
		int index = inMap.get(value);
		postIndex--;	// 移动所指向的根节点
		// 先获取右子树
		node.right = doBuildTree(index+1, inRight);
		// 再获取左子树
		node.left = doBuildTree(inLeft, index-1);
		return node;
	}
}

提交结果如下:

解答成功:
执行耗时:1 ms,击败了99.58% 的Java用户
内存消耗:43.2 MB,击败了32.11% 的Java用户

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n)n表示树的节点个数
  • 空间复杂度: O ( n ) O(n) O(n),需要使用 O ( n ) O(n) O(n)的空间存储哈希表,同时 O ( h ) O(h) O(h)的空间进行递归(即二叉树的高度),且h < n

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

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

相关文章

STM32G系 编程连接不上目标板,也有可能是软件不兼容。

由于一直用的老版本STM32 ST-LINK Utility 4.20 &#xff0c;找遍了所有问题&#xff0c;SWD就是连不上目标板。 电源脚 VDDA 地线&#xff0c;SWD的四条线&#xff0c;还是不行&#xff0c;浪费了一天&#xff0c;第二天才想起&#xff0c;是不是G系升级了 SWD协议。结果下载…

安全访问多线程环境:掌握 Java 并发集合的使用技巧

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…

[优选算法专栏]专题十五:FloodFill算法(二)

本专栏内容为&#xff1a;算法学习专栏&#xff0c;分为优选算法专栏&#xff0c;贪心算法专栏&#xff0c;动态规划专栏以及递归&#xff0c;搜索与回溯算法专栏四部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握算法。 &#x1f493;博主csdn个人主页&#xff1a;小…

【Vue3】el-checkbox-group实现权限配置和应用

一. 需求 针对不同等级的用户&#xff0c;配置不同的可见项 配置效果如下 &#xff08;1&#xff09;新增&#xff0c;获取数据列表 &#xff08;2&#xff09;编辑&#xff0c;回显数据列表 应用效果如下 &#xff08;1&#xff09;父级配置 &#xff08;2&#xff09;子级…

leetcode90. 子集 II

去重逻辑&#xff1a; 关键是画出递归树&#xff01;当我们即将进入第二个2的递归的时候&#xff0c;发现isVisit数组是100&#xff0c;也就是说这俩重复的数是False&#xff0c;并且这俩在nums值相同&#xff0c;所以写出去重逻辑&#xff01; class Solution { public:vector…

2024-2028年中国导电滑环市场行情及未来发展前景研究报告

导电滑环应用领域广泛 全球市场将保持增长趋势 导电滑环又称为集流环、集电环、导电环&#xff0c;是一种电气连接器件&#xff0c;用于在旋转部件和静止部件之间传输电能信号。导电滑环避免了传统导线在旋转中存在的磨损和扭伤&#xff0c;可提高机器运转效率和稳定性&#xf…

美易官方:通胀持续降温,美联储可能在6月份降息

近期&#xff0c;LPL首席经济学家在接受采访时表示&#xff0c;通胀持续降温&#xff0c;美联储可能在6月份降息。这一消息引起了市场的广泛关注和讨论。通胀一直是全球经济面临的难题之一&#xff0c;而美联储的货币政策也一直是市场关注的焦点。那么&#xff0c;通胀降温和美…

Discord绑VISA卡教程

Discord 是由美国 Discord Inc. 公司所开发的一款专为社群设计的免费网络实时通话软件与数字发行平台&#xff0c;主要针对游戏玩家、教育人士、朋友及商业人士&#xff0c;用户之间可以在软件的聊天频道通过讯息、图片、视频和音频进行交流 下面进行实际操作 1、登录discord …

【A-013】基于SSH的共享单车管理系统/共享单车出租系统

【A-013】基于SSH的共享单车管理系统/共享单车出租系统 开发环境&#xff1a; Eclipse/MyEclipse、Tomcat8、Jdk1.8 数据库&#xff1a; MySQL 适用于&#xff1a; 课程设计&#xff0c;毕业设计&#xff0c;学习等等 系统介绍&#xff1a; 基于SSH开发的共享单车管理系统/…

新质生产力:1核心,2摆脱,3关键,3因素,3特征;3要素,3措施

引言 新质生产力是指以科技创新为核心驱动力&#xff0c;通过提高全要素生产率、推动产业升级和转型&#xff0c;实现经济高质量发展的能力和水平。在当今全球经济竞争日趋激烈的背景下&#xff0c;新质生产力成为各国竞争力的关键之一&#xff0c;对于实现经济可持续发展、提…

CAN总线系列二:时序以及数据帧分析

由于CAN总线是异步的&#xff0c;也就是没有时钟线&#xff0c;像串口那样设置好波特率然后进行通信。因此使得其协议时序就很重要。 一、位时序 1、时序简介 为了实现位同步&#xff0c; CAN协议把每一个数据位的时序分解成SS段、 PTS段、PBS1段、 PBS2段&#xff0c;这四段…

多区域数据交换时 哪种方案可以做到便捷又可靠?

很多企业在异地都会建立分支机构&#xff0c;比如跨国企业在国外建设分公司&#xff0c;金融机构全国各地都有多级分支机构和网点&#xff0c;集团型企业会设立多家子公司&#xff0c;等等。所以这类企业都会面临多区域文件交换的场景。 多区域文件交换的场景主要包括以下几种&…

Kubernetes之Projected Volume

目录 四种Projected Volume Secret 使用方法 应用场景 示例 ConfigMap 使用方法 应用场景 示例 Downward API 使用方法 应用场景 示例 ServiceAccountToken 使用方法 应用场景 示例 在 Kubernetes 中,有几类特殊的 Volume,它们存在的意义不是为了存放容器里的…

振弦采集仪在桥梁工程监测中的优势与实践案例

振弦采集仪在桥梁工程监测中的优势与实践案例 在桥梁工程监测中&#xff0c;振弦采集仪是一种常用的监测设备。它的主要功能是通过采集桥梁振动信号&#xff0c;实时监测桥梁的结构健康状态。与传统的监测方法相比&#xff0c;振弦采集仪具有一些明显的优势&#xff0c;下面将…

docker:在ubuntu中运行docker容器

前言 1 本笔记本电脑运行的ubuntu20.04系统 2 docker运行在ubuntu20.04系统 3 docker镜像使用的是ubuntu18.04&#xff0c;这样拉的 docker pull ubuntu:18.04 4 docker容器中运行的是ubuntu18.04的系统&#xff0c;嗯就是严谨 5 这纯粹是学习笔记&#xff0c;实际上没啥价值。…

【MySQL】DQL-分组查询-语法&where与having的区别&注意事项&可cv例题语句

前言 大家好吖&#xff0c;欢迎来到 YY 滴MySQL系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C Linux的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

数据结构初阶:算法的时间复杂度和空间复杂度

什么是数据结构&#xff1f; 数据结构 (Data Structure) 是计算机存储、组织数据的方式&#xff0c;指相互之间存在一种或多种特定关系的 数据元素的集合。 什么是算法&#xff1f; 算法 (Algorithm): 就是定义良好的计算过程&#xff0c;他取一个或一组的值为输入&#xff0c…

DSSS-UQPSK学习笔记

文章目录 非平衡四相键控-直接序列扩频&#xff08;UQPSK-DSSS&#xff09;信号因其能同时传输两路不同功率、不同速率信号的特点&#xff0c;在需要图象和数据综合业务传输的领域得到了广泛应用。 系统信号的调制方式为非平衡四相键控&#xff08;Unbalanced Quadrature Phase…

【uC/OS-III篇】uC/OS-III 移植到 STM32 简明教程

uC/OS-III 移植到 STM32 简明教程 一、uC/OS-III 介绍 二、获取UCOS-III源码 三、建立项目工程 四、解决工程编译报错 五、修改项目文件 下一篇博客&#xff1a; 【uC/OS-III篇】uC/OS-III 创建第一个任务&#xff08;For STM32&#xff09; 移植后的工程自取方式&#xf…

磐启/PAN7030/2.4GHz 无线收发SOC芯片/ESSOP10/SOP16

1 概述 PAN7030 是一款集成 8 位 OTP MCU 和 2.4GHz 无线收发电路芯片&#xff0c;适合应用于玩具小车、 遥控器等领域。 PAN7030 内置 8 位 OTP MCU&#xff0c;包括 1.25KW 的程序存储器、80 字节数据存储器、16 位定 时器和 8 位/11 位 PWM 定时器、看门狗、电压比较器等…