剑指offer(四)——重建二叉树
题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
题解:
已知前序遍历和中序遍历求二叉树。
我们需要找到前序遍历数组pre[]和中序遍历数组in[]确定二叉树的规律。
根据前序遍历的性质,我们可以从pre[0]知道节点1是该树的根节点,然后在in[]中找到节点1,根据中序遍历的性质,我们可以知道in[0] ~ in[2]是该树的左子树,in[4] ~ in[7]是该树的右子树。接下从pre[1]可以知道节点2是子树in[0] ~ in[2]的根节点,从in[]中找到节点2,我们可以知道in[0] ~ in[1]是子树in[0] ~ in[2]的左子树,而且它没有右子树,可以发现,这个做法可以不断循环下去,这就是它的规律了。
因此用递归的方式来表示这个过程如下:
- 通过pre数组确定根节点
- 在in数组中找到根节点的位置,确定左右子树的范围大小
- 不断重复这个过程直至找到所有节点
代码如下
public static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
public static TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if (pre == null || in == null) {
return null;
}
// 利用HashMap来查找根节点在中序遍历中的下标,以此确定下一颗子树的范围
HashMap<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < in.length; i++) {
map.put(in[i], i);
}
return Search(pre, 0, pre.length - 1, in, 0, in.length - 1, map);
}
// (l_pre,r_pre)表示该子树在前序遍历中的下标范围,(l_in,r_in)表示该子树在中序遍历中的下标范围
public static TreeNode Search(int[] pre, int l_pre, int r_pre, int[] in, int l_in, int r_in, HashMap<Integer, Integer> map) {
// 当没有该子树时,返回null
if (l_pre > r_pre) {
return null;
}
int index = map.get(pre[l_pre]);
TreeNode head = new TreeNode(pre[l_pre]);
head.left = Search(pre, l_pre + 1, l_pre + index - l_in, in, l_in, index - 1, map);
head.right = Search(pre, l_pre + index - l_in + 1, r_pre, in, index + 1, r_in, map);
return head;
}
//输出后序遍历
public static void Print(TreeNode tree) {
if (tree == null) {
return;
}
Print(tree.left);
Print(tree.right);
System.out.println(tree.val);
}
public static void main(String[] args) {
int[] pre = new int[] {1,2,4,7,3,5,6,8};
int[] in = new int[] {4,7,2,1,5,3,8,6};
TreeNode resultTree = reConstructBinaryTree(pre, in);
Print(resultTree);
}
搜索函数主要难点在于下一颗左右子树范围的确认,以下说明助你理解。
- 因为已知下一颗子树根节点,所以在in数组中可以快速地判断下一颗左右子树的范围
- 在知道了左右子树的范围之后,就可以在pre数组中快速的表示出下一颗左右子树的范围了
- 可知pre数组中下一颗左子树的范围为本子树左边界加一到本子树左边界加上左子树范围大小,即表示为(l_pre + 1,l_pre + ((index - 1) - l_in) + 1)–>(l_pre + 1,l_pre + index - l_in)
- 可知pre数组中下一颗右子树的范围为本子树下一颗左子树的右边界加一到本子树的右边界,即表示为(l_pre + index - l_in + 1,r_pre)