这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战
重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
示例 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
题解
方法一:递归法——Java
前序遍历性质: 节点按照 [ 根节点 | 左子树 | 右子树 ] 排序。
中序遍历性质: 节点按照 [ 左子树 | 根节点 | 右子树 ] 排序。
以题目示例为例:
前序遍历划分 [ 3 | 9 | 20 15 7 ]
中序遍历划分 [ 9 | 3 | 15 20 7 ]
根据以上性质,可得出以下推论:
前序遍历的首元素 为 树的根节点 node 的值。
在中序遍历中搜索根节点 node 的索引 ,可将 中序遍历 划分为 [ 左子树 | 根节点 | 右子树 ] 。
根据中序遍历中的左(右)子树的节点数量,可将 前序遍历 划分为 [ 根节点 | 左子树 | 右子树 ]
通过以上三步,可确定 三个节点 :1.树的根节点、2.左子树根节点、3.右子树根节点。
根据「分治算法」思想,对于树的左、右子树,仍可复用以上方法划分子树的左右子树。
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return buildTreeHelp(preorder, map, 0, 0, inorder.length - 1);
}
private TreeNode buildTreeHelp(int[] preorder,
HashMap<Integer, Integer> map,
int root, int left, int right) {
if (left > right) {
return null;
}
TreeNode node = new TreeNode(preorder[root]);
int inorderRootNum = map.get(preorder[root]);
int leftRootNum = root + 1;
int rightRootNum = root + inorderRootNum - left + 1;
node.left = buildTreeHelp(preorder, map, leftRootNum, left, inorderRootNum - 1);
node.right = buildTreeHelp(preorder, map, rightRootNum, inorderRootNum + 1, right);
return node;
}
}
时间复杂度:O(N)
空间复杂度:O(N)
方法一:递归法——Go
func buildTree(preorder []int, inorder []int) *TreeNode {
if len(preorder) == 0 {
return nil
}
rootIdx := 0
for i := range inorder {
if inorder[i] == preorder[0] {
rootIdx = i
break
}
}
root := &TreeNode{Val: preorder[0]}
root.Left = buildTree(preorder[1:rootIdx+1], inorder[:rootIdx])
root.Right = buildTree(preorder[rootIdx+1:], inorder[rootIdx+1:])
return root
}
方法二:迭代法——Java
对于前序遍历中的任意两个连续节点 u 和 v,根据前序遍历的流程,我们可以知道 u 和 v 只有两种可能的关系:
-
v 是 u 的左儿子。这是因为在遍历到 uu 之后,下一个遍历的节点就是 u 的左儿子,即 v;
-
u 没有左儿子,并且 v 是 u 的某个祖先节点(或者 u 本身)的右儿子。如果 u 没有左儿子,那么下一个遍历的节点就是 u 的右儿子。如果 u 没有右儿子,我们就会向上回溯,直到遇到第一个有右儿子(且 u 不在它的右儿子的子树中)的节点 u_a,那么 v 就是u_a的右儿子。
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder == null || preorder.length == 0) {
return null;
}
TreeNode root = new TreeNode(preorder[0]);
Deque<TreeNode> stack = new LinkedList<TreeNode>();
stack.push(root);
int inorderIndex = 0;
for (int i = 1; i < preorder.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;
}
}
时间复杂度:O(N)
空间复杂度:O(N)