一. 题目
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
示例1:
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
二: 思路
所有构建二叉树问题的思路都是分三步: 找到根节点, 找到左子树, 找到右子树
这道题给了前序遍历和中序遍历的结果,我们如何利用二者的特点构建二叉树呢?
前序遍历的顺序, 根结点 --> 左子树 --> 右子树
中序遍历的顺序, 左子树 --> 根节点 --> 右子树
从二者的特点中我们可以发现,从前序遍历中很容易找到根节点(序列的第一个元素一定是根节点),
当我们找到根节点后,我们就能从中序遍历中找到他的左子树和右子树
可能大家有个疑惑,我找到的左(右)子树是个范围啊,并不是一个具体的节点,我的指针如何指向具体的节点呢?
我们可以通过递归或者dfs算法缩小这个范围,最终就能确定到一个左节点或右节点
三: 代码实现
1. 递归
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return build(preorder,inorder,0,preorder.length - 1,0,inorder.length - 1);
}
TreeNode build(int[] preorder, int[] inorder,int preStart,int preEnd,int inoStart,int inoEnd){
//base case
if(preStart > preEnd || inoStart > inoEnd){
return null;
}
//树的根节点
int val = preorder[preStart];
TreeNode root = new TreeNode(val);
//查找根节点在中序遍历的位置
int index = 0;
for(int i = inoStart;i <= inoEnd;i++){
if(inorder[i] == val){
index = i;
break;
}
}
//计算左子树在数组中的长度
int leftSize = index - inoStart;
//递归构建。
root.left = build(preorder,inorder,preStart + 1,preStart + leftSize,inoStart , index - 1);
root.right = build(preorder,inorder,preStart + leftSize + 1,preEnd,index + 1 , inoEnd);
return root;
}
}
2.分治
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 recur(0, 0, inorder.length - 1);
}
TreeNode recur(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 = recur(root + 1, left, i - 1);
// 开启右子树递归
node.right = recur(root + i - left + 1, i + 1, right);
// 回溯返回根节点
return node;
}
}