汇总:剑指offer算法合集
题目
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
示例 1:
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
示例 2:
Input: preorder = [-1], inorder = [-1]
Output: [-1]
限制:
0 <= 节点个数 <= 5000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof
解题思路
- 前序遍历的特点:先根节点,然后左子树,再到右子树,中序遍历的特点:先左子树,然后根节点,再到右子树
- 根据前序遍历的特点,我们可以知道哪个是根节点,可以在中序遍历中找到找到这个根节点,然后就可以划分出中序遍历中的左子树和右子树
- 根据中序遍历中左子树和右子树的长度,又可以在前序遍历中划分出左右子树
- 根据前序遍历划分出来的左右子树,又可以知道中序遍历中左右子树中的根节点
- 重复上面的步骤,每次发现根节点就创建这个节点,利用分治的思想,最终就能构建成一颗完整的树
- 以示例1为例:
- 根据前序遍历数组 [3,9,20,15,7],可以知道根节点为[3],可以在中序遍历数组 [9,3,15,20,7]中以3为界划分出左子树[9]和右子树[15,20,7]
- 划分出来的左子树[9]数量为1,右子树[15,20,7]数量为3,我们可以知道,在前序遍历数组 [3,9,20,15,7]中,第一个数[3]是根节点,根节点之后的1个数[9]为左子树,紧接着这个节点的后3个节点[20,15,7]为右子树
- 前序遍历划分出来的左子树[9]的第一个节点[9]就是左子树的根节点,右子树[20,15,7]的第一个节点[20]就是右子树的根节点
- 回到中序遍历划分的数组中,可以知道在中序遍历划分的数组中左子树只有一个节点[9],本身也是它的根节点,无法再划分,而右子树[15,20,7]中,[20]是根节点,因此可以继续划分左子树为[15],右子树为[7]
- 这两个子树都只有一个节点,所以它们本身就是自己的根节点,无法再划分
- 在上面的过程中,每次发现根节点就新建这个节点,重复上面的过程,最后就构建成了完整的树
复杂度分析
递归建立n个节点,因此时间复杂度为O(n)
,用HashMap保存n个数的映射关系,递归栈深度最深为n(树变成链表),综合起来空间复杂度为O(n)
代码实现
class Solution {
//用HashMap来保存中序遍历数组中值和下标的映射关系
private HashMap<Integer, Integer> rootMap = new HashMap<>();
//全局引用一下,不用每次递归都传数组
private int[] preorder;
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
//遍历中序数组,形成值和下标的映射关系,方面后面取值的下标位置
for (int i = 0; i < inorder.length; i++) {
rootMap.put(inorder[i], i);
}
//第一次根节点为前序数组的第0个数,递归整个中序数组长度,后续用分治缩减范围
return buildNewTree(0, 0, inorder.length - 1);
}
private TreeNode buildNewTree(int root, int left, int right) {
//如果left和right相等,说明只剩一个节点了,这时候创建完这个节点就结束了
//所以下一次left > right后,直接返回null不做别的操作
if (left > right) return null;
//每次创建根节点
TreeNode rootNode = new TreeNode(preorder[root]);
//从中序数组中找到根节点所在的下标
int rootIndex = rootMap.get(preorder[root]);
//左子树的根节点就是当前根节点在前序数组中下标+1,范围是原来的左边界到根节点-1
rootNode.left = buildNewTree(root + 1, left, rootIndex - 1);
//右子树的根节点是当前根节点在前序数组中的下标 + 左子树的数量 + 1,范围是当前根节点+1到原来的右边界
rootNode.right = buildNewTree(root + (rootIndex - left) + 1, rootIndex + 1, right);
return rootNode;
}
}