问题
根据一棵树的中序遍历与后序遍历构造二叉树
注意:
你可以假设树中没有重复的元素
例如,给出
中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:
3<br /> / \<br /> 9 20<br /> / \<br /> 15 7
解法一:递归
以后序数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来在切后序数组。一层一层切下去,每次后序数组最后一个元素就是节点元素
流程如图:
说到一层一层切割,就应该想到了递归
来看一下一共分几步:
- 第一步:如果数组大小为零的话,说明是空节点了
- 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素
- 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
- 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
- 第五步:切割后序数组,切成后序左数组和后序右数组
- 第六步:递归处理左区间和右区间
算法
- 为了高效查找根节点元素在中序遍历数组中的下标,我们选择创建哈希表来存储中序序列,即建立一个(元素,下标)键值对的哈希表
定义递归函数
helper(in_left, in_right)
表示当前递归到中序序列中当前子树的左右边界,递归入口为helper(0, n - 1)
:- 如果
in_left > in_right
,说明子树为空,返回空节点 - 选择后序遍历的最后一个节点作为根节点
- 利用哈希表 O(1) 查询当根节点在中序遍历中下标为
index
。从in_left
到index - 1
属于左子树,从index + 1
到in_right
属于右子树 - 根据后序遍历逻辑,递归创建右子树
helper(index + 1, in_right)
和左子树helper(in_left, index - 1)
。注意这里有需要先创建右子树,再创建左子树的依赖关系。可以理解为在后序遍历的数组中整个数组是先存储左子树的节点,再存储右子树的节点,最后存储根节点,如果按每次选择后序遍历的最后一个节点为根节点,则先被构造出来的应该为右子树 返回根节点
root
class Solution {
int post_idx;
int[] postorder;
int[] inorder;
Map<Integer, Integer> idx_map = new HashMap<Integer, Integer>();
public TreeNode helper(int in_left, int in_right) {
// 如果这里没有节点构造二叉树了,就结束
if (in_left > in_right) {
return null;
}
// 选择 post_idx 位置的元素作为当前子树根节点
int root_val = postorder[post_idx];
TreeNode root = new TreeNode(root_val);
// 根据 root 所在位置分成左右两棵子树
int index = idx_map.get(root_val);
// 下标减一
post_idx--;
// 构造右子树
root.right = helper(index + 1, in_right);
// 构造左子树
root.left = helper(in_left, index - 1);
return root;
}
public TreeNode buildTree(int[] inorder, int[] postorder) {
this.postorder = postorder;
this.inorder = inorder;
// 从后序遍历的最后一个元素开始
post_idx = postorder.length - 1;
// 建立(元素,下标)键值对的哈希表
int idx = 0;
for (Integer val : inorder) {
idx_map.put(val, idx++);
}
return helper(0, inorder.length - 1);
}
}
- 如果
时间复杂度:
,
n
是树中的节点个数- 空间复杂度:
。我们需要使用
的空间存储哈希表,以及
(其中
h
是树的高度)的空间表示递归时栈空间。这里h < n
,所以总空间复杂度为