算法题解记录11+++从前序与中序遍历序列构造二叉树(百日筑基)

本文主要是介绍算法题解记录11+++从前序与中序遍历序列构造二叉树(百日筑基),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目描述:

        给定两个整数数组 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 保证 为二叉树的中序遍历序列

解题准备:

        1.了解二叉树:二叉树是递归定义的,当其根节点root!=null时,它有以下性质:root有且仅有两颗子树,分别是左子树left和右子树right。如果把left看成树(left!=null,不过实际也是一棵树),那么其左子树也是一棵二叉树,同理,右子树right也是。

        2.了解二叉树前序、中序遍历:二叉树DFS深度优先算法有前序、中序和后序三种,其“序”的概念针对于根节点root。对于前序:root->left->right,也就是根节点、左子树、右子树;对于中序:即左子树、根节点、右子树。

        3.猜测可能存在的基础操作:由于需要构造二叉树,而且提供的是两个数组(前序遍历数组、中序遍历数组),所以大概率需要进行“添加”这一基础操作(勉强算是吧)。由于提供只两个数组,感觉可能要遍历。。所以初步认为可能有遍历、添加两个基础操作。

        4.模拟操作:

                对于下图的二叉树,我们可能轻松得到其前序、中序遍历数组(当然了,这是输入,是已知条件)

                那好像看不出什么操作:从定义开始,先寻找一下两个数组的关系。

解题难点1分析:

        难点1:如何分步操作,构造这棵二叉树?

        从定义出发,我们知道:

        1.前序数组是:【根节点,左子树,右子树】

        2.中序数组是:【左子树,根节点,右子树】

        那么,我们可以通过二者的相互关系,得到左子树前序、中序遍历数组。

        可是好像没什么用?拿到左子树的遍历数组(把它当成独立的树),好像又回到原来的问题:怎么分步构造树?

        我们知道,在左子树前、中序遍历中,一定也存在根节点,并且在前序遍历中,第一个节点就是根节点。

        那,这个性质怎么用?

        我们先了解两件事:

        1.二叉树由递归构造。

        2.对于一棵树,其所有基础操作都要落到具体的节点上,如果拿不到具体节点,什么都是假的。

        我们认为题目提供的函数已经具备构造二叉树的能力。

        也就是说,既然题目提供了前序、中序数组就能得到二叉树,不如我们借助递归去利用这个函数。

        因此,对于树root、left、right,我们把left的前、中序遍历数组,作为函数参数传递。

        不过问题来了:传递了第一遍,就没法拿到左子树left的左子树了。而且另一个问题是,前序、中序数组的构造也是个麻烦,每一个节点的左、右子树的长度不一定一样。

        也许是缺参数了,如果我们可以提供左、右子树的长度,在进行传递,就可以了?

        不行,这一步反而陷入重复论证,因为我们如果可以构造一个左子树(或右子树)数组,那么就一定知道其长度。(left.length)。

        不过我们记得:二叉树递归定义,所以其前序、中序遍历也具有递归性,对于左子树的前序遍历序列,有:

        left:【根节点left,left的左子树,left的右子树】

        把它放进根节点root中,有:【root,【(左子树)left,left左子,left右子】,右子树同理】

        也就是说,题目提供的前序、中序遍历数组,其中就包含了某个节点的前、中序遍历。

        所以,我们的问题就转换为:如何从题目提供的数组中,拿到某个节点的前、中序遍历。

        通过双指针(指向数组)就可以了,既然其具有递归性,那么某个节点的前、中序遍历是连续的(我指的是,数组中的元素连续,比如【1,2,3,4,5】,“1,2,3”连续,“1,3”不连续)

        对前序数组,用一个双指针,对中序数组,再用一个双指针,一共4个变量,就可以覆盖数据。

        因此,我们的新函数X,需要6个参数,前、中序遍历数组,4个指针。

        不过问题又来了:要怎么构造树呢?

        我们已经可以针对某个节点,得到其前、中序遍历,那么,就可以一直递归到该节点是叶子节点的时候。

        对于某个节点A,A如果是叶子节点,那么它返回其本身即可。

        A如果有子树,那么需要把子树连接到其确定位置。

        对于函数X,我们认为其具备构造二叉树的能力,只要提供给X一棵树的前、中序遍历,X就可以返回这棵树的根节点。

        因此,A有子树,如果是左子树,把左子树的前、中序遍历提供给X,然后A.left=X()即可。

        右子树同理。

        那么,如何拿到左右子树?

        1.前序:根、左、右,第一个一定是根。

        2.中序:左、根、右,拿到根,就可确定左、右子树(的长度),然后……

        由于数组不存在重复元素,所以不会出错。

        如果从前序拿到根,然后在中序中遍历,速度较慢,可以用哈希表。

代码:

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {Map<Integer, Integer> index; // 这里需要注意,如果没写泛型,默认为object,可能出错public TreeNode buildTree(int[] preorder, int[] inorder) {index=new HashMap<Integer, Integer>();int n=inorder.length;for(int i=0; i<n; i++){index.put(inorder[i], i); // 提供一个快速搜索的方式,用数组搜索也一样。}return myBuild(preorder, inorder, 0, n-1, 0, n-1);}// 递归调用private TreeNode myBuild(int[] preorder, int[] inorder, int pre_left,int pre_right, int in_left, int in_right){// 叶子节点,因为叶子节点的前、中序遍历数组为nullif(pre_right<pre_left){return null;}int root_val=preorder[pre_left]; // 得到根节点的值 int in_root=index.get(root_val); // 从中序遍历数组,拿到根节点下标int len=in_root-in_left; // 拿到节点A的遍历数组的长度TreeNode root=new TreeNode(root_val); // 实例化根节点(该根节点并非题解的根节点)root.left=myBuild(preorder, inorder, pre_left+1, pre_left+len,in_left, in_root-1); // 递归调用,前序遍历第一个是根节点,所以加1,加len是因为前、中序遍历长度相同,由于中序先是左子树,所以从left到根节点-1root.right=myBuild(preorder, inorder, pre_left+len+1, pre_right,in_root+1, in_right); // 递归调用,想拿到右子树,那么得从左子树末尾开始,后面都是右子树,所以是pre_right。中序是左根右,所以从根+1到in_rightreturn root;}
}

以上内容即我想分享的关于力扣热题11的一些知识。

        我是蚊子码农,如有补充,欢迎在评论区留言。个人也是初学者,知识体系可能没有那么完善,希望各位多多指正,谢谢大家。

这篇关于算法题解记录11+++从前序与中序遍历序列构造二叉树(百日筑基)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Java使用SLF4J记录不同级别日志的示例详解

《Java使用SLF4J记录不同级别日志的示例详解》SLF4J是一个简单的日志门面,它允许在运行时选择不同的日志实现,这篇文章主要为大家详细介绍了如何使用SLF4J记录不同级别日志,感兴趣的可以了解下... 目录一、SLF4J简介二、添加依赖三、配置Logback四、记录不同级别的日志五、总结一、SLF4J

在Spring Boot中浅尝内存泄漏的实战记录

《在SpringBoot中浅尝内存泄漏的实战记录》本文给大家分享在SpringBoot中浅尝内存泄漏的实战记录,结合实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录使用静态集合持有对象引用,阻止GC回收关键点:可执行代码:验证:1,运行程序(启动时添加JVM参数限制堆大小):2,访问 htt

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

MySQL 中查询 VARCHAR 类型 JSON 数据的问题记录

《MySQL中查询VARCHAR类型JSON数据的问题记录》在数据库设计中,有时我们会将JSON数据存储在VARCHAR或TEXT类型字段中,本文将详细介绍如何在MySQL中有效查询存储为V... 目录一、问题背景二、mysql jsON 函数2.1 常用 JSON 函数三、查询示例3.1 基本查询3.2

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

Python获取中国节假日数据记录入JSON文件

《Python获取中国节假日数据记录入JSON文件》项目系统内置的日历应用为了提升用户体验,特别设置了在调休日期显示“休”的UI图标功能,那么问题是这些调休数据从哪里来呢?我尝试一种更为智能的方法:P... 目录节假日数据获取存入jsON文件节假日数据读取封装完整代码项目系统内置的日历应用为了提升用户体验,

Spring Boot 配置文件之类型、加载顺序与最佳实践记录

《SpringBoot配置文件之类型、加载顺序与最佳实践记录》SpringBoot的配置文件是灵活且强大的工具,通过合理的配置管理,可以让应用开发和部署更加高效,无论是简单的属性配置,还是复杂... 目录Spring Boot 配置文件详解一、Spring Boot 配置文件类型1.1 applicatio

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

MySQL INSERT语句实现当记录不存在时插入的几种方法

《MySQLINSERT语句实现当记录不存在时插入的几种方法》MySQL的INSERT语句是用于向数据库表中插入新记录的关键命令,下面:本文主要介绍MySQLINSERT语句实现当记录不存在时... 目录使用 INSERT IGNORE使用 ON DUPLICATE KEY UPDATE使用 REPLACE

Python 中的异步与同步深度解析(实践记录)

《Python中的异步与同步深度解析(实践记录)》在Python编程世界里,异步和同步的概念是理解程序执行流程和性能优化的关键,这篇文章将带你深入了解它们的差异,以及阻塞和非阻塞的特性,同时通过实际... 目录python中的异步与同步:深度解析与实践异步与同步的定义异步同步阻塞与非阻塞的概念阻塞非阻塞同步