题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
限制:
0 <= 节点个数 <= 5000
解题思路
根据二叉树的前序遍历和中序遍历序列,还原二叉树,可用递归法和迭代法。其中,迭代法效率远高于递归法。
1. 递归
递归的方法非常直观。由于前序序列的第0个元素表示根节点,因此遍历中序序列,找到与前序序列第0个元素值相等的元素,即为根节点。在中序序例中,根节点左侧的序列值表示左子树,根节点右侧的序列值表示右子树。这样递归地切分前序序列和中序序列,分别找左子树和右子树即可。
要注意的是,Java中Arrays.copyOfRange(数组名,起始下标i,终止下标j),这个区间范围是[i, j),终止下标不包括在内。
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder==null || inorder==null || preorder.length<=0 || inorder.length<=0)
return null;
int len = preorder.length;
TreeNode root = new TreeNode(preorder[0]);
for(int i=0; i<len; i++){
if(inorder[i]==preorder[0]){
root.left = buildTree(Arrays.copyOfRange(preorder, 1, i+1), Arrays.copyOfRange(inorder, 0, i));
root.right = buildTree(Arrays.copyOfRange(preorder, i+1, preorder.length), Arrays.copyOfRange(inorder, i+1, inorder.length));
}
}
return root;
}
}
2. 迭代
迭代法通过栈来存储遍历到的节点。对前序序列的节点进行遍历,初识时将中序序列的指针指向中序序列的第一个节点。每次判断前序序列的元素是否等于中序序列的元素,
(1)若不相等,则前序序列的元素为上一节点的左子节点,均入栈,直到当前遍历的前序元素值与当前的中序元素值相等(初始时中序序列的第一个元素是树的最左节点),说明达到树的最左节点。
(2)若前序序列的元素等于中序序列的元素,说明已遍历到树的最左节点,此时应反向遍历前序序列(即弹出栈定元素),同时中序序列的指针++(相当于正向遍历中序序列),直到栈为空或者找到上一个根节点元素。此时,当前节点(即从栈中弹出的最后一个节点)的右子节点是前序序列的当前节点,记录并入栈。
最后返回根节点(即前序序列第0个元素表示的节点)。
代码
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder==null || inorder==null || preorder.length<=0 || inorder.length<=0)
return null;
TreeNode root = new TreeNode(preorder[0]);
int len = preorder.length;
int inorderIndex = 0;
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.push(root);
for(int i=1; i<len; i++){
TreeNode node = stack.peek();
if(node.val != inorder[inorderIndex]){
node.left = new TreeNode(preorder[i]);
stack.push(node.left);
}
else{
while(!stack.isEmpty() && stack.peek().val==inorder[inorderIndex]){
node = stack.pop();
inorderIndex++;
}
node.right = new TreeNode(preorder[i]);
stack.push(node.right);
}
}
return root;
}
}