前言:太久了都快忘了,最近做了一下笔试题遇到这题,发现经常考这种题,先把技巧记录下来,有了技巧后简直不要太轻松
1. 树的三种遍历
- 前序遍历(根左右)
- 中序遍历(左根右)
- 后序遍历(左右根)
Java实现参考这篇博客,不展开了Java实现树的遍历及二叉树可视化
下面我们来看看如何通过前序和中序推出后序以及构造二叉树
2. 前+中–>后
2.1 手写结果
不绕弯子,直接给出技巧再做题
- 看前序遍历:第一个为根节点
- 看中序遍历:找到步骤1中的根节点,左边的全为左子树,右边的全为右子树
- 将左子树看成独立的树,重复步骤1和2,直到只有一个元素,不用脑子就可以做出来啦!
下面进入实战环节(牛客网原题):
对某二叉树进行先序遍历的结果是ABDEFC,中序遍历的结果是DBFEAC,则后序遍历的结果是()
- (参照步骤1)发现根节点是A
- (参照步骤2)划分出左右子树
(参照步骤1)带着这些左子树看前序遍历,那么前序遍历的顺序是BDEF对吧,将左子树看成是一颗独立的树,于是此时由步骤1得根节点是B
(参照步骤2)在子树中继续划分出左右子树
- 还不明白?那么继续,只有一个字母的就直接填上去了,剩下FE,看前序遍历则是EF,既然已经知道是右子树,那么将右子树看成是一颗独立的树,(参照步骤1)于是此时由步骤1得根节点是E
- (参照步骤2)划分左右子树,发现只有左子树了,放哪就不必多说了
做多两题,发现掌握了技巧后画出树根本不需要脑子!
画出树后,再做后序遍历就不难了,我就不说了,答案DFEBCA
下面来看代码实现
2.2 Java实现:构造树
力扣105原题:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
老规矩,直接给模板,这是我个人在力扣评论区找到的风格比较喜欢的一套,下面的中+后–>前也是这个模板
1 | private int pre = 0, in = 0; //记录当前前序,中序的下标位置 |
只需稍微的看一下这两点
- 一个是pre和in以及他们的++
- 一个是recursive的第三个参数代表了分界点的值,也就是当前这个节点作为了它的左右子树的根节点,rootVal就是当前这个节点的值。传的参数思考过程如下:
从整棵树的根节点开始想:cur.left这一句就是前序数组中的节点作为了根节点(回想2.1介绍的东西),cur.right这一句的根节点实际上是一开始传入的节点(就是代码注释中:想象成树的根节点是无限大的某个父节点的左子树)那句,因此是传进来的rootVal。能理解吧…
那么说到这里,如何从中序+后序推出前序的代码自己都能修改了吧:pre和in一开始的位置变成末尾,++变成–,先cur.right再cur.left,改好了可以滑到3.2节去对比代码吧!
2.3 Java实现:直接获得后序结果
现在,我们可以通过前序+中序–>构建树–>后序遍历树–>获得后序遍历结果。诚然,代码的话,可以不构建树而直接获得后序遍历结果,我直接贴完整的代码
1 | public class one_zero_five_从前序与中序遍历获得后序遍历 { |
看起来吓人函数体除了注释一共才8行!!其实这个理解起来更容易,
- start为0,end为len-1,rootIndex一开始为为前序的开始所以是0,那么preorder[rootIndex]就是根节点
- 在中序中找到这个根节点,i为下标,那么inorder[i]左边为左子树,右边为右子树
- 那么左子树的范围是start ~ (i - 1),根节点在前序下标为:rootIndex +1
- 右子树的范围是(i + 1) ~ end,前序是根左右,那么右子树的根节点为:当前根节点位置 + 左子树长度 + 1,左子树长度怎么计算呢?从中序(左根右)那里计算可得i - start就是左子树长度,因此根节点下标为rootIndex + (i - start) + 1
3. 中+后–>前
3.1 手写结果
中序的作用仍然是划分左右子树,后序的作用其实跟前序一样,不过得反过来从后面开始看,最后一个才是根节点
- 看后序遍历:最后一个为根节点
- 看中序遍历:找到步骤1中的根节点,左边的全为左子树,右边的全为右子树
- 将左子树看成独立的树,重复步骤1和2,直到只有一个元素,不用脑子就可以做出来啦!
看题:
中序遍历: GBEDAFCH
后序遍历:GEDBFHCA
我就直接来了
- 看后序,根节点是A
- 看中序,左边GBED右边FCH
看后序,要先看右子树(因为后序是“左右根”,之前说了反过来看那么根完了到右),FHC说明根节点是C
看中序,左边F右边H,左右都只有一个了,完成
- 看后序,GEDB说明根节点是B
- 看中序,左边单G,右边ED
看后序,是ED,说明D是根节点
看中序,左边E,完成
3.2 Java实现:构造树
根据2.2中的套路模板,只需稍微修改一下顺序即可获得正确答案
1 | private int in, post; |
3.3 Java实现:直接获得前序结果
也是跟2.3一样的模板,理解了就好了
1 | public class one_zero_six_从中序与后序遍历获得前序遍历 { |
- start为0,end为len-1,rootIndex一开始为为后序的最后所以是len-1,那么postorder[rootIndex]就是根节点
- 在中序中找到这个根节点,i为下标,那么inorder[i]左边为左子树,右边为右子树
- 那么左子树的范围是start ~ (i - 1),根节点下标为:当前根节点下标 - 右子树长度 - 1,右子树长度怎么计算呢?从中序那里计算可得end - i就是右子树长度,因此根节点下标为rootIndex - (end - i) - 1
- 右子树的范围是(i + 1) ~ end,根节点在后序的下标为rootIndex - 1
4. 前+后–>无法确定
这种情况树不唯一
5. 附录:
搭配上个博客的可视化二叉树工具类一起食用更加,看看你做对了吗
参考/感谢:
构造树的模板提取自力扣官方题解下的朋友十三羽扬
直接获得第三序结果的代码参考了柳神的代码