105. 从前序与中序遍历序列构造二叉树
难度中等829
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
引题:
- 二叉树相关得很多问题得解决思路都有分治法得思想在里面.
- 分治法思想:把原来问题拆解成若干个与原来问题相同结构结构但是规模更小得子问题,待子问题解决以后,原问题就会解决.
- 归并排序和快速排序都是分治思想得应用. 但是’归并排序’无脑得分,在"合"得时候就会麻烦点,
- “快速排序”开始在 分 上花了很多时间,即在“分”上使了很多劲,然后就递归处理下去就好了,没有在“合”上再花时间。
抓住“前序遍历的第 1 个元素一定是二叉树的根结点”
写法一:
package com.nie.OneJanLC;/*
*
*@auth wenzhao
*@date 2021/1/17 23:20
*/
public class LEE105 {
public TreeNode buildTree(int[] preorder, int[] inorder) {
int preLen = preorder.length;
int inLen = preorder.length;
if (preLen != inLen) {
return null;
}
return retBuildTree(preorder, 0, preLen - 1, inorder, 0, inLen - 1);
}
/**
* 使用数组 preorder 在索引区间 [preLeft, preRight] 中的所有元素
* 和数组 inorder 在索引区间 [inLeft, inRight] 中的所有元素构造二叉树
*
* @param preorder 二叉树前序遍历结果
* @param preLeft 二叉树前序遍历结果的左边界
* @param preRight 二叉树前序遍历结果的右边界
* @param inorder 二叉树中序遍历结果
* @param inLeft 二叉树中序遍历结果的左边界
* @param inRight 二叉树中序遍历结果的右边界
* @return 二叉树的根结点
*/
private TreeNode retBuildTree(int[] preorder, int preLeft, int preRight,
int[] inorder, int inLeft, int inRight) {
//递归终止条件
if (preLeft > preRight || inLeft > inRight) {
return null;
}
//遍历起点元素
int pivot = preorder[preLeft];
TreeNode root = new TreeNode(pivot);
int pivotIndex = inLeft;
//判断中序遍历中得跟元素位置
while (inorder[pivotIndex] != pivot) {
pivotIndex++;
}
root.left = retBuildTree(preorder, preLeft + 1, preLeft + pivotIndex - inLeft,
inorder, inLeft, pivotIndex - 1);
root.right = retBuildTree(preorder, preLeft + pivotIndex - inLeft + 1, preRight,
inorder, pivotIndex + 1, inRight);
return root;
}
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
}
}
- 时间复杂度:O(N2)这里 NN是二叉树的结点个数,每调用一次递归方法创建一个结点,一共创建 N 个结点,在中序遍历中找到根结点在中序遍历中的位置,是与 N 相关的,这里不计算递归方法占用的时间。
- 空间复杂度:O(1),这里不计算递归方法占用的空间
写法二
可以将中序遍历得值合索引存在一个哈希表中,这样就可以找到根节点在中序遍历数组中的索引
空间换时间
package com.nie.OneJanLC;/*
*
*@auth wenzhao
*@date 2021/1/17 23:20
*/
import java.util.HashMap;
import java.util.Map;
public class LEE105L {
private int[] preprder;
private Map<Integer, Integer> hash;
public TreeNode buildTree(int[] preorder, int[] inorder) {
int preLen = preorder.length;
int inLen = preorder.length;
if (preLen != inLen) {
return null;
}
this.preprder = preorder;
this.hash = new HashMap<>();
for (int i = 0; i < inLen; i++) {
hash.put(inorder[i], i);
}
return retBuildTree(0, preLen - 1, 0, inLen - 1);
}
/**
* 使用数组 preorder 在索引区间 [preLeft, preRight] 中的所有元素
* 和数组 inorder 在索引区间 [inLeft, inRight] 中的所有元素构造二叉树
*
* @param preLeft 二叉树前序遍历结果的左边界
* @param preRight 二叉树前序遍历结果的右边界
* @param inLeft 二叉树中序遍历结果的左边界
* @param inRight 二叉树中序遍历结果的右边界
* @return
*/
private TreeNode retBuildTree(int preLeft, int preRight, int inLeft, int inRight) {
//终止条件
if (preLeft > preRight || inLeft > inRight) {
return null;
}
//中序遍历进行元素定位
int pivot = preprder[preLeft];
TreeNode root = new TreeNode(pivot);
int pivotIndex = hash.get(pivot);
root.left = retBuildTree(preLeft + 1, pivotIndex + -inLeft + preLeft,
inLeft, pivotIndex - 1);
root.right = retBuildTree(pivotIndex - inLeft + preLeft + 1, preRight,
pivotIndex + 1, inRight);
return root;
}
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
}
}
复杂度分析:
- 时间复杂度:O(N),这里 N是二叉树的结点个数,每调用一次递归方法创建一个结点,一共创建 N 个结点,这里不计算递归方法占用的时间。
- 空间复杂度:O(N),这里忽略递归方法占用的空间,因为是对数级别的,比 N 小。