白菜Java自习室 涵盖核心知识
LeetCode算法系列(Java版) 105. 从前序与中序遍历序列构造二叉树
LeetCode算法系列(Java版) 106. 从中序与后序遍历序列构造二叉树
力扣原题
106. 从中序与后序遍历序列构造二叉树
根据一棵树的中序遍历与后序遍历构造二叉树。
注意: 你可以假设树中没有重复的元素。
例如,给出
中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
分治法
二叉树相关的很多问题的解决思路都有分治法的思想在里面。
分治法的思想:把原问题拆解成若干个与原问题结构相同但规模更小的子问题,待子问题解决以后,原问题就得以解决。
此题解决问题的方式与LeetCode算法系列 105. 从前序与中序遍历序列构造二叉树类似。
解题思路
首先是一样的问题:为什么有中序遍历和后序遍历的结果,就能定义构造出整个二叉树呢?
先来复习明确中序遍历和后序遍历的含义:
- 中序遍历的顺序是 左子树->根节点->右子树。
- 后序遍历的顺序是 左子树->右子树->根节点。
注意:中序遍历的总体的 根节点->右子树 和后序遍历总体的 右子树->根节点 存在部分 重叠之处,这些重叠之处就是我们推理寻找规律的关键所在。
我们通过一个更复杂的例子,来清晰的分析解题思路:
中序遍历=[4,2,8,5,9,1,6,10,3,7]
后序遍历=[4,8,9,5,2,10,6,7,3,1]
当我们手里已明确得到中序遍历和后序遍历的结果时,能根据二叉树的特性推理得到以下规律:
-
中序遍历和后序遍历都是同一棵树的遍历结果,两个结果中树的节点定义一致;
-
后序遍历的最后一个数,必然是整个二叉树的根节点,如例子中的
1; -
如果
1是整个二叉树的根节点,中序遍历中的1也是整个二叉树的根节点,他左边[4,2,8,5,9]就是左子树,他右边[6,10,3,7]就是右子树; -
同一个根节点
1下,在中序遍历和后序遍历的结果中,根节点的左子树和右子树的节点个数必然一样,中序遍历的左子树[4,2,8,5,9]为5个节点,右子树[6,10,3,7]为4个节点,所以后序遍历的左子树也为5个节点[4,8,9,5,2],右子树也为4个节点[10,6,7,3]; -
此时问题已经分治为几个小的相同的问题,显然能作为递归的结构去处理。
- 首先这个二叉树的根节点为
1; - 根节点
1的左子树,中序遍历结果为[4,2,8,5,9],后序遍历结果为[4,8,9,5,2]; - 根节点
1的右子树,中序遍历结果为[6,10,3,7],中序遍历结果为[10,6,7,3];
- 如何划分数组的界限,我们可以定义不同的下标(游标)来实现编码。中序遍历左右
inLeft、inRight,后序遍历左右postLeft、postRight。
代码实现
import java.util.HashMap;
import java.util.Map;
public class Solution {
/**
* 让 postorder 成为全局变量,以免在递归方法中一直传递
*/
private int[] postorder;
private Map<Integer, Integer> hash;
public TreeNode buildTree(int[] inorder, int[] postorder) {
int inLen = inorder.length;
int postLen = postorder.length;
if (inLen != postLen) {
throw new RuntimeException("输入错误");
}
this.postorder = postorder;
hash = new HashMap<>();
for (int i = 0; i < inLen; i++) {
hash.put(inorder[i], i);
}
return buildTree(0, inLen - 1, 0, postLen - 1);
}
/**
* 使用中序遍历序列 inorder 的子区间 [inLeft, inRight]
* 与后序遍历序列 postorder 的子区间 [postLeft, postRight] 构建二叉树
*
* @param inLeft 中序遍历序列的左边界
* @param inRight 中序遍历序列的右边界
* @param postLeft 后序遍历序列的左边界
* @param postRight 后序遍历序列的右边界
* @return 二叉树的根结点
*/
private TreeNode buildTree(int inLeft, int inRight, int postLeft, int postRight) {
if (inLeft > inRight || postLeft > postRight) {
return null;
}
int pivot = postorder[postRight];
int pivotIndex = hash.get(pivot);
TreeNode root = new TreeNode(pivot);
root.left = buildTree(inLeft, pivotIndex - 1, postLeft, postRight - inRight + pivotIndex - 1);
root.right = buildTree(pivotIndex + 1, inRight, postRight - inRight + pivotIndex, postRight - 1);
return root;
}
}
复杂度分析
-
时间复杂度:,这里 是二叉树的结点个数,二叉树的每个结点都遍历了 2 次,第 1 次是扫描构建哈希表,第 2 次是递归构建二叉树;
-
空间复杂度:。
LeetCode算法系列(Java版) 105. 从前序与中序遍历序列构造二叉树
LeetCode算法系列(Java版) 106. 从中序与后序遍历序列构造二叉树