今天在刷剑指offer的时候,刷到了重建二叉树这道题,题目的要求很简单,根据二叉树的前序遍历以及中序遍历重构二叉树。
自己刚做这种算法类的问题不久,刚上来的思路也不是很明确,摸索着根据左右子树是否存在这样的条件来确定二叉树的结点,思虑了好久,发现这种方法并不行。无奈下看了csnote上的答案。ps:总觉得这道题很简单,自己好笨啊。
csnote上cyc大神给的代码很精简,思路也很清晰,首先根据前序遍历确定根节点,然后确定根节点在中序遍历的位置,分出左右子树,最后再对左右子树递归地进行上述的过程,乍一看思路很简单,这里借用csnote上的示意图。![递归重建二叉树](https://i-blog.csdnimg.cn/blog_migrate/dafe90bb2de17b5ae2c1e6409251f22d.gif)
对于确定根节点在中序遍历中的位置很自然便想到可以使用HashMap来方便地解决上述问题,我们可以利用一个类变量HashMap来存储中序遍历中各节点,其中键为各个节点的关键字,值为节点的坐标。之后,我们需要确定递归需要哪些变量来进行运算,以及递归的终结束条件。
1、确定根节点在中序遍历中的位置可以将中序遍历分为左右两部分,其中左半部分的长度就可以理解为左子树节点的数目,右半部分同理。因此我们要确定左、右子树每部分的开始与终止位置,以便对每部分分别进行递归
2、同时,我们要明白一件事,就是前序遍历各半部分子树的遍历一定是连续的,也就是说某节点的左子树的全部节点一定在右子树的全部节点之前(似乎是废话)。因此我们只要确定了根节点,根节点的下一个节点(如果存在的话)一定是左子树,根节点加左子树的节点数的开始位置就是右子树的根节点。
3、因此,看起来我们需要每个子树根节点的位置,以及根节点结束的位置,树的节点数,以及中序遍历中子树的开始与结束位置。
4、实际上,左子树的节点数可以由根节点在中序遍历中的位置减去该子树在中序遍历中起始位置来确定,因此也可以顺理成章地确定根节点左节点的位置(根节点加1)以及右节点的位置(根节点加左子树节点数);此时左子树在中序遍历中的起始位置不变,但右子树在中序遍历中开始的位置变为根节点在中序遍历中的位置加1,而实际上我们也不需要特意声明中序遍历结束的位置,因为中序遍历开始的位置与子树的全部结点数(根据前序遍历开始结束位置确定)就可以确定
5、这样我们递归函数的变量就变为前序遍历开始、结束的位置,中序遍历中开始的位置三个变量。
6、当前序遍历开始的位置小于结束的位置,则证明这颗子树已经生成到头了,也即返回null值,否则就根据开始的位置生成根节点,这也就是递归函数的终止条件,之后递归的调用递归函数生成左右子树。
这道题的关键点在于:
1、每棵子树在前序遍历中一定是连续的
2、根节点在中序遍历中位置的左右两部分分别为其左右子树
3、根据上面条件递归地去生成左右节点
这样写代码的好处是简洁,减少了变量的重复定义,但是解释起来却比较麻烦,个人推荐自己写的话,变量多一点其实没关系,重要的是容易理解,毕竟不是每个人都是大神嘛。下面是代码部分。
import java.util.Map;
import java.util.HashMap;
// Definition for binary tree
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
}
public class Solution {
private Map<Integer, Integer> indexForInOrders = new HashMap<>();
public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
for (int i = 0; i < in.length; i++)
indexForInOrders.put(in[i], i);
return reConstructBinaryTree(pre, 0, pre.length - 1, 0);
}
//inL为该子树在中序遍历中开始的位置
private TreeNode reConstructBinaryTree(int[] pre, int preL, int preR, int inL) {
if (preL > preR)
return null;
TreeNode root = new TreeNode(pre[preL]);
int inIndex = indexForInOrders.get(root.val);
int leftTreeSize = inIndex - inL;
//root.left = reConstructBinaryTree(pre, preL + 1, preL + leftTreeSize, inL);
root.left = reConstructBinaryTree(pre, preL + 1, preL + leftTreeSize, inL);
root.right = reConstructBinaryTree(pre, preL + leftTreeSize + 1, preR, inIndex + 1);
return root;
}
}