题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
重建二叉树
上午在学习广度遍历优先的算法,看到可以通过该算法层级遍历二叉树,于是想写个二叉树来测试一下算法,刚好算法群的群主很灵性,今天就发了这么一道打卡题,真是[一箭双雕」,美滋滋。
前序,中序,后序
简单说一下二叉树的遍历方式,按照根节点的遍历顺序不同,可以分为三种
- 前序,根节点->左节点->右节点
- 中序,左节点->根节点->右节点
- 后序,左节点->右节点->根节点
可以参照例题中的二叉树验证一下各种遍历方式。
如何确认二叉树
给定一个二叉树,我们能确定它的前序,中序,后序的遍历序列;反过来,给定前序,中序,后序中的一个,能否确认一个二叉树,答案是:不能。
因为要倒推出一个二叉树,至少需要:
- 确认根节点
- 左,右子树的节点情况
至少需要给定两个遍历序列才能确认二叉树,并且不是随意两个序列的组合
- 前序 and 中序,满足上述条件
- 中序 and 后序,满足上述条件
- 前序 and 后续,不满足上述条件
例题中举的二叉树例子,我们试着来推导一下
根据前序遍历的规则,我们可以确认首个元素是根节点,再根据根节点在中序中的位置去推断左右子树的元素
接着我们需要来写出推算公式,画到这里突然发现我举的这个例子不太直观,这边借用一下 leetcode 大佬画的图(这个画图软件是他推荐给我的,叫做 omnigraffle,逼格非常高)
由上图我们可知:
- 通过 [preL + 1, pivot - inL + preL] , [inL, pivot - 1] 两个区间,可以递归往下确认左子树
- 通过 [pivot - inL + preL + 1, preR] ,[pivot + 1, inR] 两个区间,可以递归往下确认右子树
实现代码如下:
class Node {
public int value;
public Node left;
public Node right;
public Node(int val) {
value = val;
}
}
public class BinaryTree {
Node root;
int[] preOrder;
HashMap<Integer, Integer> inOrderMap = new HashMap<>();
public Node buildTree(int[] preorder, int[] inorder) {
this.preOrder = preorder;
// 存储中序中元素的索引位置,方便后续查找根节点索引不用重复遍历
for (int i = 0; i < inorder.length; i++) {
inOrderMap.put(inorder[i], i);
}
this.root = buildTree(0, preOrder.length - 1, 0, inorder.length - 1);
}
public Node buildTree(int preL, int preR, int inL, int inR) {
if (preL > preR || inL > inR) return null;
Node root = new Node(preOrder[preL]);
// 计算根节点在中序中的位置
int pivotIndex = inOrderMap.get(root.value);
// 构造左子树
root.left = buildTree(preL + 1, pivotIndex - inL + preL, inL, pivotIndex - 1);
// 构造右子树
root.right = buildTree(pivotIndex - inL + preL + 1, preR, pivotIndex + 1, inR);
return root;
}
题目链接
https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/