【题目描述】
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
限制:0 <= 节点个数 <= 5000
【样例示例】
给出
- 前序遍历:preorder = [3,9,20,15,7]
- 中序遍历:inorder = [9,3,15,20,7]
返回如下的二叉树:
【解题思路】
树节点遍历的性质有三种:前中后序
- 前序遍历顺序:根节点 => 左节点 => 右节点
- 中序遍历顺序:左节点 => 根节点 => 右节点
- 后序遍历顺序:左节点 => 右节点 => 根节点
- 根据上述的性质,前序数组第一个元素
preorder[0]
就是树的根节点。 - 知道了根节点,就可以根据中旬遍历结果划分左右子树了,在根节点左边的就是树的左子树,根节点右边的是树的右子树。例如上面给出的样例中,根节点就是
3
,在inorder
数组中,3左边的就是左子树的结点,3右边的是右子树的所有节点。 - 基于以上,采用递归获取树的其余节点
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
public class Solution {
int[] preorder; // 存放前序遍历结果
HashMap<Integer, Integer> dic = new HashMap<>();// 存放中序遍历结果
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
for(int i = 0; i < inorder.length; i++)
dic.put(inorder[i], i);
return back(0, 0, inorder.length - 1);
}
public TreeNode back(int root, int left, int right) {
//递归结束条件
if(left > right)
return null;
// 根节点
TreeNode node = new TreeNode(preorder[root]);
// 在中序遍历数组中找到根节点位置,并以此划分左右子树
int i = dic.get(preorder[root]);
// 左子树递归
node.left = back(root + 1, left, i - 1);
// 右子树递归
node.right = back(root + i - left + 1, i + 1, right);
// 返回根节点
return node;
}
}
需要注意的地方:
左子树递归的划分比较好理解,左子树的根节点看前序遍历数组,就是左子树的第一个root+1
,左边边界就是left
,右边边界是中间区分的i-1
;
右子树的根节点可能难理解一点,其实右子树的根也是看前序遍历数组,就是右子树的第一个,即当前根节点加上左子树的数量
- 左子树的数量 = 左子树的右边 - 左边 =
i-1-left+1
=i-left
- 右子树的根 = 当前根节点+左子树数量+1 =
root+i-left+1
所以右子树第一个根是
root+i-left+1
,左边边界是i+1
,右边边界是right
参考文章: