1.问题
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
原题链接;
2.解法
方法一:
这是一个递归问题,前序遍历是ceter-left-right的方式遍历,中序遍历是left-ceter-right(这里的ceter是指父节点,left,right是左右子树)。
因此,前序遍历的第一个数是父节点的值,由于树的各个节点值唯一,根据中序遍历,找到父节点所在的索引位置,就可以判断左子树,右子树了。同样的,对左,右子树分别进行这样的操作,就可以把原来的树进行补全了。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder.length == 0){
return null;
}
TreeNode resTree = new TreeNode(0);
resTree.val = preorder[0];
int i = 0;
for(; i< inorder.length; i++){
if(inorder[i] == preorder[0]){
break;
}
}
resTree.left = buildTree(Arrays.copyOfRange(preorder, 1, 1+i),
Arrays.copyOfRange(inorder, 0, i));
resTree.right = buildTree(Arrays.copyOfRange(preorder, 1+i, preorder.length),
Arrays.copyOfRange(inorder, 1+i, inorder.length));
return resTree;
}
}
时间复杂度 O(n);
空间复杂度 O(n);
方法二:
方法一需要额外的空间来存储子数组;但是,可以通过索引和指针来传递。
递归思路:
用 left,right
记录一棵树在中序遍历的索引范围,只有从先序遍历知道根节点,就能在中序遍历中知道左子树、右子树的索引范围,从而实现递归;
preroot
代表这棵树的根节点在先序遍历的索引值。
用遍历的方式,找到根节点在中序遍历的索引值。
这里的方法弄的复杂了,因此,这里传入的数组还是原来的数组,只更改树对应索引范围,不提倡。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder.length ==0){
return null;
}
return generateTree(preorder, inorder, 0, preorder.length-1, 0 );
}
public TreeNode generateTree(int[] preorder, int[] inorder, int left, int right, int preroot){
if(left == right ){
TreeNode tmpLeft = new TreeNode(preorder[preroot]);
return tmpLeft;
}
if(left>right) return null;
int cut = 0;
cut = findPos(inorder, preorder[preroot], left, right);
//left
TreeNode ans = new TreeNode(preorder[preroot]);
ans.left = generateTree(preorder, inorder, left, cut-1, preroot+1);
//right
ans.right = generateTree( preorder, inorder, cut+1, right, preroot+cut-left+1 );
return ans;
}
//用遍历的方式,找到根节点在中序遍历的索引值, start和end 代表搜索的范围。
public int findPos(int[] arr, int val, int start, int end){
int pos = start;
for(; pos <= end; pos++){
if(arr[pos] == val){
break;
}
}
return pos;
}
}
空间复杂度:O(1)
方法三:
思路和方法二差不多,只不过用 hashMap
来记录中序遍历,提高速度。
class Solution {
// start from first preorder element
int pre_idx = 0;
int[] preorder;
int[] inorder;
HashMap<Integer, Integer> idx_map = new HashMap<Integer, Integer>();
public TreeNode helper(int in_left, int in_right) {
// if there is no elements to construct subtrees
if (in_left == in_right)
return null;
// pick up pre_idx element as a root
int root_val = preorder[pre_idx];
TreeNode root = new TreeNode(root_val);
// root splits inorder list
// into left and right subtrees
int index = idx_map.get(root_val);
// recursion
pre_idx++;
// build left subtree
root.left = helper(in_left, index);
// build right subtree
root.right = helper(index + 1, in_right);
return root;
}
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
this.inorder = inorder;
// build a hashmap value -> its index
int idx = 0;
for (Integer val : inorder)
idx_map.put(val, idx++);
return helper(0, inorder.length);
}
}