这里主要是要往分治上想,并且联系到中序序列和前序序列的关系。
我们知道中序序列,对于val而言,出现在val左边的值都在它的左子树上,出现在右侧的值都在它的右子树上。
那么我们考虑,遍历中序序列,将中序序列的值和其出现的索引位置映射,这样,我们就能比较容易的分割开左子树和右子树。
我们获取到值val在中序序列中的索引idx,假定我们选取的这一段中序序列区间为\([l...r]\),那么左子树的长度len为\(idx - l\)。
由于同一颗子树的前序遍历长度和中序遍历长度一定是一样的,因此我们可以把所求得的长度len运用到前序遍历的结果中去。
利用以下三个结论:
①.前序遍历的首元素 为 树的根节点 node 的值。
②.在中序遍历中搜索根节点 node 的索引 ,可将 中序遍历 划分为 [ 左子树 | 根节点 | 右子树 ] 。
③.根据中序遍历中的左(右)子树的节点数量,可将 前序遍历 划分为 [ 根节点 | 左子树 | 右子树 ] 。
因此代码如下所示:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
// 存储中序遍历的节点和索引关系
Map<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return build(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1, map);
}
private TreeNode build(int[] preorder, int preStart, int preEnd, int[] inorder, int inStart, int inEnd, Map<Integer, Integer> map) {
if(preStart > preEnd || inStart > inEnd) {
return null;
}
int rootVal = preorder[preStart];
TreeNode root = new TreeNode(rootVal);
// 获取root在中序遍历结果中的位置
int midIndex = map.get(rootVal);
// 获取左子树的长度
int leftLength = midIndex - inStart;
root.left = build(preorder, preStart + 1, preStart + leftLength, inorder, inStart, midIndex - 1, map);
root.right = build(preorder, preStart + leftLength + 1, preEnd, inorder, midIndex + 1, inEnd, map);
return root;
}
}