今天通过这道题来研究研究前序遍历,中序遍历之间到底有什么关系
题目描述:
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
题目示例:
我先说一下解题思路,当然,你要是想彻底了解下面的过程,那么你应该要对二叉树的前中后序遍历了然于心,不是很清楚的,可以先读一下:《剑指 Offer 54. 二叉搜索树的第k大节点》,这道题目弄懂之后在回来看看这道题。
我们整个过程就是不停的通过前序遍历去找根节点,然后通过中序遍历去划分左右子树,我通过题目示例来演示一下:
-
1.首先通过前序遍历确定第一个根节点(i=0):pre[0]=3
然后通过中序遍历找到3在中序遍历中的位置:[9,3,15,20,7]
所以我们就可以划分出第一棵子树:
-
2.在接续找下一棵树,首先你要心里清楚,以3位根节点的是我们要确定的第一棵子树,再接着找下一棵子树(i=1),pre[1]=9,也就是说下一棵我们要确定的树是以9为根节点,确定好了根节点,我们就要通过中序遍历确定左右子树,[9,3,15,20,7],可以发现9没有左右子树
-
3.再接着通过 前序遍历的序列确定下一个根节点(i=2)pre[2]=20
在中序遍历的范围内找到20来划分左右子树:[9,3,15,20,7],于是树又进一步被我们划分为下图所示的样子:
-
4.再根据前序遍历接着找下一个根节点(i=3)pre[3]=15:
[9,3,15,20,7]
我们会发现15同样没有左右子树 -
5.再根据前序遍历接着找下一个根节点(i=4)pre[4]=7:
[9,3,15,20,7]
通过上面的步骤,其实不难发现我们一直在经历两个过程,在前序遍历序列找根节点,在中序遍历序列划分左右子树。
代码实现如下:
public class 剑指Offer07_重建二叉树 {
Map<Integer, Integer> map;
public TreeNode buildTree(int[] preorder, int[] inorder) {
int length = preorder.length;
if (length <= 0) return null;
if (length <= 1) return new TreeNode(preorder[0]);
map = new HashMap<>();
for (int i = 0; i < length; i++) {//找到中序遍历下对应的根节点位置
map.put(inorder[i], i);
}
return dfs(preorder, 0, length - 1,
0, length - 1);
}
/**
* 递归的去构建二叉树
*
* @param preorder
* @param inorder
* @param pre_left 前序遍历范围 的第一个节点位置
* @param pre_right 前序遍历范围的最后一个位置
* @param in_left 中序遍历范围内的第一个位置
* @param in_right 中序遍历范围内的最后一个位置
* @return
*/
public TreeNode dfs(int[] preorder, int pre_left, int pre_right,
int in_left, int in_right) {
if (pre_left > pre_right) {//循环终止条件,说明根节点已经遍历结束了
return null;
}
//确定根节点
int pre_root = pre_left;//前序遍历下根节点的位置
//构建根节点
TreeNode root = new TreeNode(preorder[pre_root]);
int in_root = map.get(preorder[pre_root]);//旨在判断左右子树的长度
//获取左子树的长度
int length_left = in_root - in_left;
//开始递归的重复这个操作构建左右子树
/**
* 因为前序遍历左边的第一个节点我们已经用过了,所以要找下一个根节点
* 同时我们也可以确定左子树的长度为:len_left
*/
root.left = dfs(preorder, pre_left+1, pre_left + length_left,
in_left, in_root - 1);
root.right = dfs(preorder, pre_left + length_left + 1, pre_right,
in_root + 1, in_right);
return root;
}
}