#LeetCode每日一题【二叉树专题】
- 从前序与中序遍历序列构造二叉树
https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ - 分析
前序:根——左——右
中序:左——根——右
前序的第一个元素肯定是根节点,在中序中,根节点左边的元素是左子树的所有元素,根节点右边的元素是右子树的所有元素,在前序序列中找到左子树、右子树的前序序列,在递归的处理左右子树即可 - 实现
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder.length == 0) {
return null;
}
int rootValue = preorder[0], len = preorder.length;
if (len == 1) {
return new TreeNode(rootValue);
}
// 找到左子树、右子树的中序
int location = 0;
for (int i = 0; i < len; i++) {
if (inorder[i] == rootValue) {
location = i;
break;
}
}
int[] leftInorder = new int[location];
int[] rightInorder = new int[len - location - 1];
System.arraycopy(inorder, 0, leftInorder, 0, location);
System.arraycopy(inorder, location + 1, rightInorder, 0, rightInorder.length);
// 找到左子树的先序、右子树的先序
int[] leftPreorder = new int[location];
int[] rightPreorder = new int[rightInorder.length];
System.arraycopy(preorder, 1, leftPreorder, 0, leftPreorder.length);
System.arraycopy(preorder, 1 + leftPreorder.length, rightPreorder, 0, rightPreorder.length);
TreeNode left = buildTree(leftPreorder, leftInorder);
TreeNode right = buildTree(rightPreorder, rightInorder);
return new TreeNode(rootValue, left, right);
}
使用了大量的数组copy
LeetCode耗时:6ms
- 优化
涉及从原数组中copy一个新数组进行递归处理的,完全可以使用数组的下标来代替,开始下标、结束下标表示一个数组的范围。
public TreeNode buildTreeOptimize(int[] preorder, int[] inorder) {
if (preorder == null || preorder.length == 0) {
return null;
}
return buildTree(preorder, inorder, 0, preorder.length - 1, 0, inorder.length - 1);
}
private TreeNode buildTree(int[] preorder, int[] inorder, int preorderStart, int preorderEnd,
int inorderStart, int inorderEnd) {
// 递归结束条件
if (preorderStart > preorderEnd) {
return null;
}
// 先序的第一个元素是根节点
int rootValue = preorder[preorderStart];
// 根节点在中序中的位置
int rootLocation = getIndexFromArray(rootValue, inorder, inorderStart, inorderEnd);
// 左子树节点数、先序中序数组的长度
int leftLength = rootLocation - inorderStart;
// 找到左右子树先序中序开始、结束的下标
TreeNode left = buildTree(preorder, inorder, preorderStart + 1, preorderStart + leftLength,
inorderStart, rootLocation - 1);
TreeNode right = buildTree(preorder, inorder, preorderStart + leftLength + 1, preorderEnd,
rootLocation + 1, inorderEnd);
return new TreeNode(rootValue, left, right);
}
private int getIndexFromArray(int rootValue, int[] inorder, int inorderStart, int inorderEnd) {
for (int i = inorderStart; i <= inorderEnd; i++) {
if (inorder[i] == rootValue) {
return i;
}
}
return -1;
}
LeetCode耗时:3ms
- 在优化
看了题解,将中序遍历的结果先用一个map存储起来,便于后面每次可以根据值快速检索到下标,以空间换时间
Map<Integer, Integer> map = new HashMap<>();
// 思路是正确的,找左子树的先序、中序;右子树的先序、中序,在递归地处理
// 原方法是每次递归处理时都从原数组中复制出一个新的数组,存在大量的拷贝耗时
// 基于这种情况,可以使用下标移动的方式,代替从原数组中进行拷贝,进行优化一下
// 需要知道本轮递归先序开始的下标、结束的下标;中序开始的下标、结束的下标;
public TreeNode buildTreeOptimize(int[] preorder, int[] inorder) {
if (preorder == null || preorder.length == 0) {
return null;
}
// 每次都需要从中序序列中找到根节点的位置,需要循环遍历一篇,可以考虑使用空间换时间,使用map提前存储好下标对应的值
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return buildTree(preorder, inorder, 0, preorder.length - 1, 0, inorder.length - 1);
}
private TreeNode buildTree(int[] preorder, int[] inorder, int preorderStart, int preorderEnd,
int inorderStart, int inorderEnd) {
// 递归结束条件
if (preorderStart > preorderEnd) {
return null;
}
// 先序的第一个元素是根节点
int rootValue = preorder[preorderStart];
// 根节点在中序中的位置
// int rootLocation = getIndexFromArray(rootValue, inorder, inorderStart, inorderEnd);
Integer rootLocation = map.get(rootValue);
// 左子树节点数、先序中序数组的长度
int leftLength = rootLocation - inorderStart;
// 找到左右子树先序中序开始、结束的下标
TreeNode left = buildTree(preorder, inorder, preorderStart + 1, preorderStart + leftLength,
inorderStart, rootLocation - 1);
TreeNode right = buildTree(preorder, inorder, preorderStart + leftLength + 1, preorderEnd,
rootLocation + 1, inorderEnd);
return new TreeNode(rootValue, left, right);
}
LeetCode耗时:1ms
- 总结
- 熟悉前序中序后序的特性,从中找到构造树的规律
- 熟悉树的递归的应用
- 涉及从原数组中copy一个新数组进行递归处理的,完全可以使用数组的下标来代替,开始下标、结束下标表示一个数组的范围