【LetMeFly】889.根据前序和后序遍历构造二叉树:分治(递归)——双O(n)的做法,五彩斑斓的题解(若不是彩色的可以点击原文链接查看)

本文主要是介绍【LetMeFly】889.根据前序和后序遍历构造二叉树:分治(递归)——双O(n)的做法,五彩斑斓的题解(若不是彩色的可以点击原文链接查看),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【LetMeFly】889.根据前序和后序遍历构造二叉树:分治(递归)——双O(n)的做法,五彩斑斓的题解(若不是彩色的可以点击原文链接查看)

力扣题目链接:https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-postorder-traversal/

给定两个整数数组,preorder 和 postorder ,其中 preorder 是一个具有 无重复 值的二叉树的前序遍历,postorder 是同一棵树的后序遍历,重构并返回二叉树。

如果存在多个答案,您可以返回其中 任何 一个。

 

示例 1:

输入:preorder = [1,2,4,5,3,6,7], postorder = [4,5,2,6,7,3,1]
输出:[1,2,3,4,5,6,7]

示例 2:

输入: preorder = [1], postorder = [1]
输出: [1]

 

提示:

  • 1 <= preorder.length <= 30
  • 1 <= preorder[i] <= preorder.length
  • preorder 中所有值都 不同
  • postorder.length == preorder.length
  • 1 <= postorder[i] <= postorder.length
  • postorder 中所有值都 不同
  • 保证 preorder 和 postorder 是同一棵二叉树的前序遍历和后序遍历

方法一:分治(递归)——双O(n)的做法

做这道题前强烈建议先去做一下从前序与中序建树。我们知道:

  • 前序遍历: 左子树 右子树
  • 后序遍历:左子树 右子树

我们需要明白:左子树和右子树只有一个的情况下,仅仅通过前序遍历和后续遍历的结果是无法得到原树是左子还是右子的。这是因为,对于某个只有一个子树的节点:

  • 假设此节点只有左子树,那么遍历结果为:前序【 左子树】后序【左子树
  • 假设此节点只有右子树,那么遍历结果为:前序【 右子树】后序【右子树

仅凭遍历结果无法得知到底是左子树还是右子树

因此我们可以按照“只有一个子树的情况下将其还原为左子树”的方式进行建树。

因此我们可以写一个函数dfs接收前序遍历数组后序遍历数组作为参数:

  1. 前序遍历数组的第一个节点(或者说后序遍历数组的最后一个节点)为

  2. 如果前序遍历数组的长度为1,那么说明只有节点,直接返回。

  3. 否则必定存在左子树(前面我们得出了结论,即使只有一个子树也可以视为左子树)。因此我们只需要得到左子树右子树(可能为空但无所谓)的长度,就能在前序遍历数组后序遍历数组中将二者划分出来,并继续递归。确定左子树长度的方法为:

    前序遍历数组中,左子树的第一个节点为左子树的根节点。

    后序遍历数组中,左子树的最后一个节点为左子树的根节点。

    因此从前序遍历数组中可以得到左子树的根节点,由这个节点在后序遍历数组中的位置,能得到左子树的长度。

    从而右子树的长度也能从任意一个遍历数组中,减去左子树的长度(减节点的长度)得出。

递归的终止条件为“前序遍历数组为空”,此时返回空节点。

Tips: 可以在预处理时建立一个哈希表,以便能快速地找到左子树的根节点在后序遍历数组中的位置。

  • 时间复杂度 O ( N ) O(N) O(N),其中 N N N是节点个数
  • 空间复杂度 O ( N ) O(N) O(N)

AC代码

C++
class Solution {
private:unordered_map<int, vector<int>::iterator> ma;TreeNode* dfs(vector<int>::iterator preLeft, vector<int>::iterator preRight, vector<int>::iterator postLeft, vector<int>::iterator postRight) {if (preLeft >= preRight) {return nullptr;}if (preLeft + 1 == preRight) {  // 只有一个节点return new TreeNode(*preLeft);}int leftLength = leftLength = ma[*(preLeft + 1)] - postLeft + 1;  // 注意是*(preLeft + 1)return new TreeNode(*preLeft,dfs(preLeft + 1, preLeft + 1 + leftLength, postLeft, postLeft + leftLength),dfs(preLeft + 1 + leftLength, preRight, postLeft + leftLength, postRight - 1));}
public:TreeNode* constructFromPrePost(vector<int>& preOrder, vector<int>& postOrder) {for (vector<int>::iterator it = postOrder.begin(); it != postOrder.end(); it++) {ma[*it] = it;}return dfs(preOrder.begin(), preOrder.end(), postOrder.begin(), postOrder.end());}
};
Python
# from typing import List, Optional# # Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = rightclass Solution:def dfs(self, preOrder: List[int], preLeft: int, preRight: int, postOrder: List[int], postLeft: int, postRight: int) -> Optional[TreeNode]:if preLeft >= preRight:return Noneif preLeft + 1 == preRight:return TreeNode(preOrder[preLeft])leftLength = self.ma[preOrder[preLeft + 1]] - postLeft + 1return TreeNode(preOrder[preLeft],self.dfs(preOrder, preLeft + 1, preLeft + 1 + leftLength, postOrder, postLeft, postLeft + leftLength),self.dfs(preOrder, preLeft + 1 + leftLength, preRight, postOrder, postLeft + leftLength, postRight - 1))def constructFromPrePost(self, preOrder: List[int], postOrder: List[int]) -> TreeNode:self.ma = dict()for i in range(len(postOrder)):self.ma[postOrder[i]] = ireturn self.dfs(preOrder, 0, len(preOrder), postOrder, 0, len(postOrder))

同步发文于CSDN,原创不易,转载经作者同意后请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/136227823

这篇关于【LetMeFly】889.根据前序和后序遍历构造二叉树:分治(递归)——双O(n)的做法,五彩斑斓的题解(若不是彩色的可以点击原文链接查看)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

MySQL存储过程之循环遍历查询的结果集详解

《MySQL存储过程之循环遍历查询的结果集详解》:本文主要介绍MySQL存储过程之循环遍历查询的结果集,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言1. 表结构2. 存储过程3. 关于存储过程的SQL补充总结前言近来碰到这样一个问题:在生产上导入的数据发现

Windows的CMD窗口如何查看并杀死nginx进程

《Windows的CMD窗口如何查看并杀死nginx进程》:本文主要介绍Windows的CMD窗口如何查看并杀死nginx进程问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录Windows的CMD窗口查看并杀死nginx进程开启nginx查看nginx进程停止nginx服务

如何确定哪些软件是Mac系统自带的? Mac系统内置应用查看技巧

《如何确定哪些软件是Mac系统自带的?Mac系统内置应用查看技巧》如何确定哪些软件是Mac系统自带的?mac系统中有很多自带的应用,想要看看哪些是系统自带,该怎么查看呢?下面我们就来看看Mac系统内... 在MAC电脑上,可以使用以下方法来确定哪些软件是系统自带的:1.应用程序文件夹打开应用程序文件夹

Linux查看系统盘和SSD盘的容量、型号及挂载信息的方法

《Linux查看系统盘和SSD盘的容量、型号及挂载信息的方法》在Linux系统中,管理磁盘设备和分区是日常运维工作的重要部分,而lsblk命令是一个强大的工具,它用于列出系统中的块设备(blockde... 目录1. 查看所有磁盘的物理信息方法 1:使用 lsblk(推荐)方法 2:使用 fdisk -l(

MySQL查看表的最后一个ID的常见方法

《MySQL查看表的最后一个ID的常见方法》在使用MySQL数据库时,我们经常会遇到需要查看表中最后一个id值的场景,无论是为了调试、数据分析还是其他用途,了解如何快速获取最后一个id都是非常实用的技... 目录背景介绍方法一:使用MAX()函数示例代码解释适用场景方法二:按id降序排序并取第一条示例代码解

查看MySql主从同步的偏移量方式

《查看MySql主从同步的偏移量方式》:本文主要介绍查看MySql主从同步的偏移量方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 1.mysql的主从同步方案mysqlphp为了在实现读写分离,主库写,从库读mysql的同步方案主要是通过从库读取主库的binl

python进行while遍历的常见错误解析

《python进行while遍历的常见错误解析》在Python中选择合适的遍历方式需要综合考虑可读性、性能和具体需求,本文就来和大家讲解一下python中while遍历常见错误以及所有遍历方法的优缺点... 目录一、超出数组范围问题分析错误复现解决方法关键区别二、continue使用问题分析正确写法关键点三

一文详解如何查看本地MySQL的安装路径

《一文详解如何查看本地MySQL的安装路径》本地安装MySQL对于初学者或者开发人员来说是一项基础技能,但在安装过程中可能会遇到各种问题,:本文主要介绍如何查看本地MySQL安装路径的相关资料,需... 目录1. 如何查看本地mysql的安装路径1.1. 方法1:通过查询本地服务1.2. 方法2:通过MyS

mysql递归查询语法WITH RECURSIVE的使用

《mysql递归查询语法WITHRECURSIVE的使用》本文主要介绍了mysql递归查询语法WITHRECURSIVE的使用,WITHRECURSIVE用于执行递归查询,特别适合处理层级结构或递归... 目录基本语法结构:关键部分解析:递归查询的工作流程:示例:员工与经理的层级关系解释:示例:树形结构的数

查看MySQL数据库版本的四种方法

《查看MySQL数据库版本的四种方法》查看MySQL数据库的版本信息可以通过多种方法实现,包括使用命令行工具、SQL查询语句和图形化管理工具等,以下是详细的步骤和示例代码,需要的朋友可以参考下... 目录方法一:使用命令行工具1. 使用 mysql 命令示例:方法二:使用 mysqladmin 命令示例:方