0x01.问题
从前序与中序遍历序列构造二叉树
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出:
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
0x02.算法详细分析
我们知道,根据前序序列和中序序列是可以完全确定一棵二叉树,那么我们是怎么根据前序序列和中序序列去确定这个这棵树的呢?我们一起来探究一下。
1.第一个问题:我们要明白,前序序列和中序序列是怎么得到的?
-
这应该是比较基础的二叉树知识了。
-
二叉树的前序序列得到的方式:
- 遍历根节点。
- 递归遍历左子树。
- 递归遍历右子树。
-
二叉树的中序序列得到的方式:
- 递归遍历左子树。
- 遍历根节点。
- 递归遍历右子树。
2.第二个问题:前序序列和中序序列中的值是怎样对应原来树中的结构的呢?
-
一定要将这个序列和原来的树结合起来,知道序列中的值是如何分布的,才能够还原原来的那棵二叉树。
-
根据上面得到序列的方式,我们应该不难得到,下面的这个关系:
-
也就是说,前序序列的根节点在最前面,然后是左子树,然后是右子树。
-
中序序列中先是前序序列,然后是根节点,然后是右子树。
-
知道序列中是怎么分布后,我们似乎还没有能够构造出完整的树的思路,因为我们似乎遇到了一个困难:
- 假如我们第一个以前序序列的第一个节点构造了树的根节点,下一个节点如何寻找?根节点的左右子树到底在序列的哪个位置?
3.深度思考一下第三个问题:如何去序列中寻找对应的左右子树?
-
既然根节点是前序序列的第一个节点,那么根节点的左子树如何寻找?
- 其实很简单了,左子树就是前序序列的第二个节点,因为前序序列就是先遍历左子树,再遍历右子树,所以,左子树就很好寻找了。
-
那么右子树如何去寻找呢?
- 我们最原始的想法很简单,就是去找到这个位置就可以了。
- 我们最原始的想法很简单,就是去找到这个位置就可以了。
-
因为这个位置是右子树的第一个节点,也就是根节点的右子树。
-
那么问题来了,中间这段距离怎么求,也就是左子树的数目。
-
不要想当然的认为左子树和右子树的数量是相等的,因为问题只说了是二叉树,并没有说是什么特殊的二叉树,所以并不能确定左子树的数量。
-
这个时候,不要忘记,我们还有一个中序序列,这个中序序列和前序序列所对应的二叉树可是相同的,所以自然左子树的数目也是相同的。
-
我们的想法是,要是知道根节点在中序序列中的位置,左子树的数量就可以确定下来了。
-
而题目中说了,保证节点的值是唯一的,所以,只要一个个找,肯定是能找到的。
-
但是这样查找就比较浪费时间,我们可以维护一个哈希表,每次去哈希表中查找,就能把时间的复杂度降下来。
4.具体的算法思路?
-
在上面的分析中,我们确定了基本的思路,也就是依次寻找根节点,左子树,右子树。
-
而在树中,这个过程肯定是可以抽取出来的,也就是可以使用递归的方式将这个过程表示出来。
-
细节: 每一次递归需要哪些参数呢?
- 由于需要确定序列中左右子树的边界,所以对于每种序列来说,左右边界当然是需要的。
-
细节:初始化?
- 初始化条件是
0,n-1
。
- 初始化条件是
-
细节:递归退出的条件?
- 当左边界大于右边界时,递归退出。
0x03.算法–从前序与中序遍历序列构造二叉树
class Solution {
private Map<Integer,Integer> indexMap;
private TreeNode toBuildTree(int[] preorder,int[] inorder,int pre_left,int pre_right,int in_left,int in_right){
//递归终止条件
if(pre_left>pre_right){
return null;
}
//建立根节点
TreeNode root=new TreeNode(preorder[pre_left]);
//得到中序序列中根节点的下标
int in_root=indexMap.get(preorder[pre_left]);
//得到前序序列中左子树的长度
int size_left=in_root-in_left;
//递归建立左子树
root.left=toBuildTree(preorder,inorder,pre_left+1,pre_left+size_left,in_left,in_root-1);
//递归建立右子树
root.right=toBuildTree(preorder,inorder,pre_left+size_left+1,pre_right,in_root+1,in_right);
return root;
}
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n=preorder.length;
if(n!=inorder.length){
return null;
}
indexMap=new HashMap<>();
for(int i=0;i<n;i++){
indexMap.put(inorder[i],i);
}
return toBuildTree(preorder,inorder,0,n-1,0,n-1);
}
}