LeetCode——105.从前序与中序遍历序列构造二叉树
题目
105.从前序与中序遍历序列构造二叉树
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
解析
图来自LeetCode官方题解
递归创建。思路如上图:
通过前序数组来确定根节点,然后在中序数组中找到根节点,中序数组通过根节点的下标就可以确定左子树和右子树和下标范围,然后递归调用就可以了。前序数组中左子树和右子树的下标范围公式自己算一下就明白了。
代码
/**
* 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) {
//判断输入数据是否正确,可以不写
int preorderLen = preorder.length; //获得两个数组的长度
int inorderLen = inorder.length;
if (preorderLen != inorderLen){ //判断长度是否相等
throw new RuntimeException("Incorrect input data!");
}
//创建二叉树
return creatTree(preorder, inorder,0, preorder.length-1, 0, inorder.length-1);
}
/**
*
* @param preorder 前序遍历的数组
* @param inorder 中序遍历的数组
* @param preLeft 要使用的前序遍历数组的起始下标
* @param preRight 要使用的前序遍历数组的结束下标
* @param inLeft 要使用的中序遍历数组的起始下标
* @param inRight 要使用的中序遍历数组的结束下标
* @return
*/
private TreeNode creatTree(int[] preorder, int[] inorder, int preLeft, int preRight, int inLeft, int inRight) {
//递归终止条件
if (preLeft > preRight || inLeft > inRight){
return null;
}
//递归体
int pIndex = 0; //存放中序数组中根节点的下标
// 前序数组中的第一个节点就是根节点
int rootVal = preorder[preLeft]; //获取根节点的值
// 先把根节点建立出来
TreeNode root = new TreeNode(rootVal);
// 在中序数组中定位根节点下标(这一步可以用Map优化)
for (int i = inLeft; i <= inRight; i++) {
if (rootVal == inorder[i]){ //如果找到根节点
pIndex = i; //记录中序数组中根节点的下标
break;
}
}
//这里传入新的下标的公式,可以自己在草稿纸上算一下。
//前序和中序数组的左子树区间
root.left = creatTree(preorder, inorder, preLeft+1, pIndex-inLeft+preLeft, inLeft, pIndex-1);
//前序和中序数组的右子树区间
root.right = creatTree(preorder, inorder, pIndex-inLeft+preLeft+1, preRight, pIndex+1, inRight);
return root;
}
}
使用HashMap进行优化
/**
* 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) {
//判断输入数据是否正确,可以不写
int preorderLen = preorder.length; //获得两个数组的长度
int inorderLen = inorder.length;
if (preorderLen != inorderLen){ //判断长度是否相等
throw new RuntimeException("Incorrect input data!");
}
//使用map存储中序数组,帮助我们快速定位根节点
Map<Integer,Integer> map = new HashMap<>(preorder.length);
for (int i = 0; i < preorder.length; i++) {
map.put(inorder[i],i); //注意这里是把中序数组中的值当做键,下标当做值,便于查找
}
//创建二叉树
return creatTree(preorder, map,0, preorder.length-1, 0, inorder.length-1);
}
/**
*
* @param preorder 前序遍历的数组
* @param map 存放中序遍历数组的map
* @param preLeft 要使用的前序遍历数组的起始下标
* @param preRight 要使用的前序遍历数组的结束下标
* @param inLeft 要使用的中序遍历数组的起始下标
* @param inRight 要使用的中序遍历数组的结束下标
* @return
*/
private TreeNode creatTree(int[] preorder, Map<Integer,Integer> map, int preLeft, int preRight, int inLeft, int inRight) {
//递归终止条件
if (preLeft > preRight || inLeft > inRight){
return null;
}
//递归体
// 前序数组中的第一个节点就是根节点
int rootVal = preorder[preLeft]; //获取根节点的值
// 先把根节点建立出来
TreeNode root = new TreeNode(rootVal);
// 在中序数组中定位根节点下标
int pIndex = map.get(rootVal);
//这里传入新的下标的公式,可以自己在草稿纸上算一下。
//前序和中序数组的左子树区间
root.left = creatTree(preorder, map, preLeft+1, pIndex-inLeft+preLeft, inLeft, pIndex-1);
//前序和中序数组的右子树区间
root.right = creatTree(preorder, map, pIndex-inLeft+preLeft+1, preRight, pIndex+1, inRight);
return root;
}
}