题目:给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
题目已经提供了二叉树的前序遍历(preorder)和中序遍历(inorder),需要我们把二叉树构造出来。
根据二叉树的特性, 已知前序遍历和中序遍历,或者已知后序遍历和中序遍历,都是可以确定唯一的二叉树的。
大概能想到要通过前序和中序节点的顺序来确定根节点和左右子节点,比如前序遍历的第一个元素(3)一定是二叉树的根节点。
但是想了一会还是不知道怎么做,直接根据答案学习思路:
1.先从前序遍历中获取二叉树的根节点3,然后在中序遍历中找到根节点的位置(第二个),此时能确定9是左子树中的节点,而15、20、7是右子树中的节点。
2.使用分治策略,对确定的左子树(9)和右子树(15,20,7), 运用同样的方法,可以继续找到这两个子树的根节点以及对应的左右子树
总之使用递归+分治的方法,不断获取到下一层二叉树的根节点,直到最后获取到最下层的节点。
首先需要定义一个hashmap,在每次获取到当前层级的根节点后,找到它在中序遍历中的位置。
private Map<Integer,Integer> indexMap;
然后将中序遍历的所有元素【已出现的位置】:【节点的值的形式】存入hashmap中。这样存放好后会返回{0:9, 1:3, 2:15, 3:20, 4:7}
indexMap = new HashMap<>();
for (int i = 0; i < inorder.length; i++) {
indexMap.put(inorder[i], i);
}
定义一个递归方法实现分治,每次获取到当前层的根节点,然后让根节点的左子树节点和右子树节点调用自身方法。
方法输入的参数:完整前序遍历的数组,完整中序遍历的数组,前序遍历开始位置,前序遍历结束位置,中序遍历开始位置,中序遍历结束位置
public TreeNode build(int[] preorder, int[] inorder, int leftp, int rightp, int lefti, int righti){
}
从前序遍历中取出根节点,并在中序遍历中获取到它的位置(第一次调用的时候leftp传入0,代表此时二叉树根节点是preorder[0]),由此可以获得在中序遍历数组中确定根节点的位置。
int preorder_root = leftp;
int inorder_root = indexMap.get(preorder[preorder_root]);
将该节点的值(3)作为二叉树根节点的值,然后通过“inorder_root - lefti”获取到这棵二叉树左子树的区间(9)和右子树的区间(15,20,7)。
解释:lefti最开始也取值为0,“inorder_root - lefti” = 1-0 =1,代表根节点为3的二叉树,左子树的元素为1个。右子树同理
TreeNode root = new TreeNode(preorder[preorder_root]);
int size_left_subtree = inorder_root - lefti; int size_right_subtree = righti - inorder_root;
确定好左右子节点在前序遍历和中序遍历中的区间后,对左右子节点调用自身:
root.left = build(preorder, inorder, leftp + 1, leftp + size_left_subtree, lefti, inorder_root - 1); root.right = build(preorder, inorder,rightp-size_right_subtree+1, rightp, inorder_root+1,righti);
最后return root
return root;
还要做一些其他校验,比如leftp不可以大于rightp,传入的数组参数不能为null。
完整代码:
class Solution {
private Map<Integer,Integer> indexMap;
public TreeNode buildTree(int[] preorder, int[] inorder) {
indexMap = new HashMap<>();
for (int i = 0; i < inorder.length; i++) {
indexMap.put(inorder[i], i);
}
if (preorder == null || preorder.length == 0) {
return null;
}
if (inorder == null || inorder.length == 0) {
return null;
}
// 构造哈希映射,帮助我们快速定位根节点
indexMap = new HashMap<Integer, Integer>();
for (int i = 0; i < preorder.length; i++) {
indexMap.put(inorder[i], i);
}
return build(preorder, inorder, 0, preorder.length - 1, 0, preorder.length - 1);
}
public TreeNode build(int[] preorder, int[] inorder, int leftp, int rightp, int lefti, int righti)
{
if (leftp > rightp) {
return null;
}
int preorder_root = leftp;
int inorder_root = indexMap.get(preorder[preorder_root]);
TreeNode root = new TreeNode(preorder[preorder_root]);
int size_left_subtree = inorder_root - lefti;
int size_right_subtree = righti - inorder_root;
root.left = build(preorder, inorder, leftp + 1, leftp + size_left_subtree, lefti, inorder_root - 1);
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root.right = build(preorder, inorder, rightp - size_right_subtree + 1, rightp, inorder_root + 1, righti);
return root;
}
}