题目地址:https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/
题目
- 从中序与后序遍历序列构造二叉树
根据一棵树的中序遍历与后序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
思路分析:
首先,来一棵树
然后再看树的遍历结果
根据中序和后序遍历结果还原二叉树
中序遍历和后续遍历的特性
首先来看题目给出的两个已知条件中序遍历序列和后序遍历序列 根据这两种遍历的特性我们可以得出两个结论
- 在后序遍历序列中,最后一个元素为树的根节点
- 在中序遍历序列中,根节点的左边为左子树,根节点的右边为右子树
如下图所示:
树的还原过程变量定义
需要定义几个变量帮助我们进行树的还原
1.HashMap memo 需要一个哈希表来保存中序遍历序列中,元素和索引的位置关系.因为从后序序列中拿到根节点后,要在中序序列中查找对应的位置,从而将数组分为左子树和右子树
2.int ri 根节点在中序遍历数组中的索引位置
3.中序遍历数组的两个位置标记 [is, ie],is是起始位置,ie是结束位置
4.后序遍历数组的两个位置标记 [ps, pe] ps是起始位置,pe是结束位置
位置关系的计算
在找到根节点位置以后,我们要确定下一轮中,左子树和右子树在中序数组和后续数组中的左右边界的位置。
- 左子树-中序数组 is = is, ie = ri - 1
- 左子树-后序数组 ps = ps, pe = ps + ri - is -1 (pe计算过程解释,后续数组的起始位置加上左子树长度-1 就是后后序数组结束位置了,左子树的长度 = 根节点索引-左子树)
- 右子树-中序数组 is = ri + 1, ie = ie
- 右子树-后序数组 ps = ps + ri - is, pe - 1
听不明白没关系,看图就对了,计算图示如下
树的还原过程
代码如下所示:
class Solution {
HashMap<Integer,Integer> memo = new HashMap<>();
int[] post;
public TreeNode buildTree(int[] inorder, int[] postorder) {
for(int i = 0;i < inorder.length; i++) memo.put(inorder[i], i);
post = postorder;
TreeNode root = buildTree(0, inorder.length - 1, 0, post.length - 1);
return root;
}
public TreeNode buildTree(int is, int ie, int ps, int pe) {
if(ie < is || pe < ps) return null;
int root = post[pe];
int ri = memo.get(root);
TreeNode node = new TreeNode(root);
node.left = buildTree(is, ri - 1, ps, ps + ri - is - 1);
node.right = buildTree(ri + 1, ie, ps + ri - is, pe - 1);
return node;
}
}
难点分析:
这道题主要的难点就是在于数组边界的划分。需要我们自己把图画出来,一个个的把数组的范围确立并且用对应的变量表达。
还使用的哈希表来存放中序遍历,便于在之后找到根节点在中序遍历数组中的下标。
另外一种方法
这种方法不用确定后序遍历数组的边界。所以在递归时传递的参数比较好,但理解起需要费一些脑力。
后序是左–右--根的特点,所以后序遍历从后向前是根–右--左,故在递归时根本没有必要将后序遍历的数组界限传入,每次有节点加入就自减一就行,只要满足了先拿到根节点然后遍历右子树,再遍历左子树这个原则就Okay。
Map<Integer, Integer> map = new HashMap<>();//存放中序的映射
int pindex = 0;//后序的索引
public TreeNode buildTree(int[] inorder, int[] postorder) {
pindex = postorder.length - 1;//因为二叉树的第一个节点对应后序的最后一个,从最后一个开始
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return build(postorder, 0, inorder.length - 1);
}
/**
*
* @param postorder 后序链表
* @param inl 中序的左边界
* @param inr 中序的右边界
* @return 二叉树节点
*/
public TreeNode build(int[] postorder, int inl, int inr) {
if (inl > inr) return null;
TreeNode node = new TreeNode(postorder[pindex--]);//从最后一个开始初始化为TreeNode。
//注意上面的postorder[pindex--],此处相当于postorder[pindex],pindex--;
int index = map.get(node.val);//得到在中序的索引
node.right = build(postorder, index + 1, inr);//先遍历右子树
node.left = build(postorder, inl, index - 1);//再遍历左子树
return node;
}