「这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战」
前言
力扣第105题 从前序与中序遍历序列构造二叉树 如下所示:
给定一棵树的前序遍历 preorder 与中序遍历 inorder。请构造二叉树并返回其根节点。
示例 1:
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
一、思路
题目意思很明确,就是根据前序遍历和中序遍历,还原出二叉树
题目中有一个需要注意的提示:preorder 和 inorder 均无重复元素
我们其实可以想一个问题,为什么靠前序遍历不能还原一个二叉树呢?
举个例子,假设前序遍历的结果为
[1, 2, 3]。你能还原二叉树吗?很明显是不能的,因为会有如下的两种情况。第一种情况:
第二种情况:
但是如果告诉你前序遍历的结果为
[1, 2, 3],中序遍历的结果为[2, 1, 3]。那么很容易就知道,这样还原的结果为第一种情况。
我们知道前序遍历和中序遍历有如下的特点:
- 前序遍历:根左右
- 中序遍历:左根右
又因为二叉树中没有重复的节点值,所以 前序遍历中的第一个节点为根节点。所以在中序遍历中在根节点前面的值为左子树,右边的值为右子树。这样就能还原出二叉树了。
图解算法
此处以示例一中的 preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] 作为例子。
-
根据前序遍历的特性,很容易就知道第一个节点
3是根节点 -
将中序遍历中的结果根据根节点分割,分为左子树和右子树
- 因为左子树只有一个节点
9,直接作为叶子节点即可。我们知道左子树只有一个节点,所以右子树的根节点为前序遍历的第三个节点。
- 右子树在分割为左右子树即可,如下图:
- 最终的结果如下所示:
二、实现
实现代码
实现代码与思路基本保持一致,唯一需要注意的是:我们先使用了
HashMap来存储中序遍历中个节点的位置,防止重复的在中序遍历中寻找目标位置
private Map<Integer, Integer> inMap;
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n = preorder.length;
int m = inorder.length;
// 构造哈希映射,记住中序遍历中每个节点的位置
inMap = new HashMap<>();
for (int j=0; j<m; j++) {
inMap.put(inorder[j], j);
}
return dfs(preorder, 0, n - 1, 0);
}
/**
*
* @param left 中序遍历中左子树起始位置
* @param right 中序遍历中右子树起始位置
* @param rootIndex 前序遍历中根节点值
* @return
*/
public TreeNode dfs(int[] preorder, int left, int right, int rootIndex) {
if (left > right) {
return null;
}
int inorder_root = inMap.get(preorder[rootIndex]);
TreeNode root = new TreeNode(preorder[rootIndex]);
root.left = dfs(preorder, left, inorder_root-1, rootIndex+1);
root.right = dfs(preorder, inorder_root+1, right, rootIndex+inorder_root-left+1);
return root;
}
测试代码
public static void main(String[] args) {
int[] preorder = {3, 9, 20, 15, 7};
int[] inorder = {9, 3, 15, 20 ,7};
TreeNode treeNode = new Number105().buildTree(preorder, inorder);
System.out.println("test");
}
结果
三、总结
感谢看到最后,非常荣幸能够帮助到你~♥
如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~