赞
踩
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例1:
输入:preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出:[3,9,20,null,null,15,7]
一般来说涉及二叉树的遍历问题的话,我们应该首先都可以想到利用递归的方法来切入,因为二叉树就其本身而言就是一个递归性质的树,我们可以首先抛开编码,设想自己在画树的时候如何构建一个给出前中序遍历的树。
首先我们来回顾一下二叉树的遍历方法
- 前序遍历:先遍历根节点,然后遍历其左子树,最后遍历右子树
- 中序遍历:先遍历其左子树,然后遍历根节点,最后是遍历右子树
- 后序遍历:先遍历其左子树,后遍历其右子树,最后遍历根节点
不难发现常规的二叉树遍历都是左子树位于右子树之前(注意非常规的情况)。分析上面的遍历性质,很容易可以得到,对于前序遍历来说,第一个元素必然是树的根节点,而对于一个树的中序遍历来说,其根节点必然中序遍历序列分为左右两个部分,分别对应左子树和右子树。在此基础上所有的子树也是遵照这个性质的我们知道了这一点性质就可以用来解题了。我们总结出以下递归方法
n
前序序列中找到当前树的根节点,再找到根节点在中序序列中的位置i; 此时中序序列中的0~i-1
就是左子树,i ~n
就是右子树root
节点,然后继续在中序序列中划分左右子树的左右子树。(我们需要考虑子树为空的情况)拿上面的示例分析,我们首先确定前序序列中的第一个元素3为树的根节点,此时在中序序列中找到3的位置并按照其将序列划分为左右两部分分别为左右子树
3,9,20,15,7
[9],3,[15,20,7]
此时再构造左子树,左子树只有一个节点,其自己为自己的根节点;
继续构造右子树,查询前序序列发现20为右子树的根节点,再在中序序列中查询20,并按照这个节点将右子树划分为左右两部分,分别为15和7,至此树构造完成。
3,9,20,15,7
[9],3,[15],20,[7]
在解题中对于中序遍历中的根节点位置求法我首先使用的是使用find
函数库函数来进行查找,但是find函数的返回值是一个迭代器指针,使用的话还要继续再求迭代器距离,较为繁琐也容易出错,参考官方解答使用一个hashmap
首先对于每个节点的位置进行存储,这样就可以牺牲一些空间来达到代码的简洁性,时间上来说可能也更高效。另外只要使用到指针我们必然要避免指针溢出的问题,尤其是指针参与到循环当中,必然要格外注意这个问题。在实际解题过程中,我们必然会碰到左子树或者右子树为空的情况,此时我们就要理清指针溢出的思路。假如是一个子树为空,我们可以得到其preorderLeft
必然是大于preorderRight
的,所以在每次递归前我们首先要对此做出判定,否则会因指针指向而出错。
我们也可以采用非递归的方式来进行解题,不过非递归的方式的处理逻辑就比较复杂一点了。跟递归方法一样,也是要以前序为基础,中序为参照来划分树的节点。首先来观察二叉树的前序遍历序列,我们会发现,对于两个相邻的节点u和v
,他们的关系有以下几种情况:
假设有下面的一个树
我们接下来分析上面的树的构建,首先我们维护一个栈st
来保存没有处理过右孩子的节点,中序遍历处理下标index=0
。
这里解释一下index的含义,如果index所指的值与栈顶元素相同,就说明这个栈顶元素必然没有左孩子(此时中序序列中index指向的值左边都已经处理过,也就是该节点中序遍历左边是没有元素了),此时就可以说明当前处理的节点
preorder[i]
是一个右孩子。
当index所指的值与栈顶元素不相同,就说明栈顶元素是有左孩子的,而且pr
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。