[面试题07. 重建二叉树]
难度 中等
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
限制:
0 <= 节点个数 <= 5000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof
解法一:递归
二叉树的前序遍历为:根节点、左子树、右子树
二叉树的中序遍历为:左子树、根节点、右子树
前序遍历的第一个节点是根节点,只要找到根节点在中序遍历中的位置,就可以区分左子树和右子树分别有多少个节点。
由于树中的节点数量与遍历方式无关,通过中序遍历得知左子树和右子树的节点数量之后,可以根据节点数量得到前序遍历中的左子树和右子树的分界,因此可以进一步得到左子树和右子树各自的前序遍历和中序遍历,可以通过递归的方式,重建左子树和右子树,然后重建整个二叉树。
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder == null || preorder.length == 0) {
return null;
}
return buildTree(preorder, 0, preorder.length - 1, inorder, 0, preorder.length - 1);
}
private TreeNode buildTree(int[] preorder,int preStart, int preEnd, int[] inorder, int inStart, int inEnd) {
//递归出口, 遍历序列中不包含任何节点
if(preStart > preEnd || inStart > inEnd) {
return null;
}
int index = 0;
//寻找前序序列中第一个节点在中序序列中的位置
for(index = inStart; index <= inEnd; index++) {
if(preorder[preStart] == inorder[index]) {
break;
}
}
TreeNode temp = new TreeNode(preorder[preStart]);
//创建左子树
temp.left = buildTree(preorder, preStart + 1, preStart + index - inStart, inorder, inStart, index - 1);
//创建右子树
temp.right = buildTree(preorder, preStart + index - inStart + 1, preEnd, inorder, index + 1, inEnd);
return temp;
}
}
时间复杂度:O(n) 对于每个节点都有创建过程以及根据左右子树重建过程。
空间复杂度:O(n) 存储整棵树的开销。
改进:可以将中序遍历的值和索引存在一个哈希表中,这样就可以很方便的找到根结点在中序遍历数组中的索引。
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder == null || preorder.length == 0) {
return null;
}
Map<Integer, Integer> indexMap = new HashMap<Integer, Integer>();
for (int i = 0; i < preorder.length; i++) {
indexMap.put(inorder[i], i);
}
return function(preorder, 0, preorder.length - 1, inorder, 0, preorder.length - 1, indexMap);
}
private TreeNode function(int[] preorder,int preStart, int preEnd, int[] inorder, int inStart, int inEnd, Map<Integer, Integer> indexMap) {
//递归出口, 遍历序列中不包含任何节点
if(preStart > preEnd || inStart > inEnd) {
return null;
}
//寻找前序序列中第一个节点在中序序列中的位置
int index = indexMap.get(preorder[preStart]);
TreeNode temp = new TreeNode(preorder[preStart]);
//创建左子树
temp.left = function(preorder, preStart + 1, preStart + index - inStart, inorder, inStart, index - 1, indexMap);
//创建右子树
temp.right = function(preorder, preStart + index - inStart + 1, preEnd, inorder, index + 1, inEnd, indexMap);
return temp;
}
}
解法二:迭代
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder == null || preorder.length == 0) {
return null;
}
TreeNode root = new TreeNode(preorder[0]);
int length = preorder.length;
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.push(root);
int inorderIndex = 0;
for (int i = 1; i < length; i++) {
int preorderVal = preorder[i];
TreeNode node = stack.peek();
if (node.val != inorder[inorderIndex]) {
node.left = new TreeNode(preorderVal);
stack.push(node.left);
} else {
while (!stack.isEmpty() && stack.peek().val == inorder[inorderIndex]) {
node = stack.pop();
inorderIndex++;
}
node.right = new TreeNode(preorderVal);
stack.push(node.right);
}
}
return root;
}
}