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

2024-04-01 09:44

本文主要是介绍LC 106.从中序与后序遍历序列构造二叉树,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

这篇关于LC 106.从中序与后序遍历序列构造二叉树的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/866829

相关文章

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

关于最长递增子序列问题概述

《关于最长递增子序列问题概述》本文详细介绍了最长递增子序列问题的定义及两种优化解法:贪心+二分查找和动态规划+状态压缩,贪心+二分查找时间复杂度为O(nlogn),通过维护一个有序的“尾巴”数组来高效... 一、最长递增子序列问题概述1. 问题定义给定一个整数序列,例如 nums = [10, 9, 2

C++中使用vector存储并遍历数据的基本步骤

《C++中使用vector存储并遍历数据的基本步骤》C++标准模板库(STL)提供了多种容器类型,包括顺序容器、关联容器、无序关联容器和容器适配器,每种容器都有其特定的用途和特性,:本文主要介绍C... 目录(1)容器及简要描述‌php顺序容器‌‌关联容器‌‌无序关联容器‌(基于哈希表):‌容器适配器‌:(

uva 10131 最长子序列

题意: 给大象的体重和智商,求体重按从大到小,智商从高到低的最长子序列,并输出路径。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vect

POJ1631最长单调递增子序列

最长单调递增子序列 import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWriter;import java.math.BigInteger;import java.util.StringTokenizer;publ

leetcode105 从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。 注意: 你可以假设树中没有重复的元素。 例如,给出 前序遍历 preorder = [3,9,20,15,7]中序遍历 inorder = [9,3,15,20,7] 返回如下的二叉树: 3/ \9 20/ \15 7   class Solution {public TreeNode buildTree(int[] pr

PHP实现二叉树遍历(非递归方式,栈模拟实现)

二叉树定义是这样的:一棵非空的二叉树由根结点及左、右子树这三个基本部分组成,根据节点的访问位置不同有三种遍历方式: ① NLR:前序遍历(PreorderTraversal亦称(先序遍历)) ——访问结点的操作发生在遍历其左右子树之前。 ② LNR:中序遍历(InorderTraversal) ——访问结点的操作发生在遍历其左右子树之中(间)。 ③ LRN:后序遍历(PostorderT

day-50 求出最长好子序列 I

思路 二维dp,dp[i][h]表示nums[i] 结尾,且有不超过 h 个下标满足条件的最长好子序列的长度(0<=h<=k),二维数组dp初始值全为1 解题过程 状态转换方程: 1.nums[i]==nums[j],dp[i,h]=Math.max(dp[i,h],dp[j,h]+1) 2.nums[i]!=nums[j],dp[i,h]=Math.max(dp[i,h],dp[j,h-1

LeetCode:3177. 求出最长好子序列 II 哈希表+动态规划实现n*k时间复杂度

3177. 求出最长好子序列 II 题目链接 题目描述 给你一个整数数组 nums 和一个非负整数k 。如果一个整数序列 seq 满足在下标范围 [0, seq.length - 2] 中 最多只有 k 个下标i满足 seq[i] != seq[i + 1] ,那么我们称这个整数序列为好序列。请你返回 nums中好子序列的最长长度。 实例1: 输入:nums = [1,2,1,1,3],

react笔记 8-17 属性绑定 class绑定 引入图片 循环遍历

1、绑定属性 constructor(){super()this.state={name:"张三",title:'我是一个title'}}render() {return (<div><div>aaaaaaa{this.state.name}<div title={this.state.title}>我是一个title</div></div></div>)} 绑定属性直接使用花括号{}   注