题目链接
参考Krahets题解
先序遍历顺序: [根节点|左子树|右子树]
中序遍历顺序: [左子树|根节点|右子树]
例如题目中
- 前序遍历划分 [ 3 | 9 | 20 15 7 ]
- 中序遍历划分 [ 9 | 3 | 15 20 7 ]
基于这两个序列的性质,我们可以设计如下算法
- 按序访问前序遍历序列,得到根节点root
- 在中序遍历序列查找root,得到
[左子树,root,右子树]
- 根据2中得到的左右子树长度,可以将前序遍历序列划分为
[root,左子树,右子树]
这里步骤2中查找root的方法有两种,一种是直接按序查找中序遍历序列,时间复杂度为 O ( n ) O(n) O(n),另一种则是根据哈希表查找,时间复杂度为 O ( 1 ) O(1) O(1)。会得到不同的算法复杂度。
按序查找的方法
这里的preorder
和inorder
数组反复不变地出现在递归参数中,应该设置为成员变量,避免过长的递归参数。造成递归内存的浪费
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder == null || inorder == null || preorder.length == 0 || inorder.length == 0
|| preorder.length != inorder.length)
return null;
return buildTree(preorder, inorder, 0, preorder.length - 1, 0, inorder.length - 1);
}
private TreeNode buildTree(int[] preorder, int[] inorder, int startPreorderIndex,
int endPreorderIndex, int startInorderIndex, int endInorderIndex){
// 新建节点的值,从先序遍历中取出对应的值
int rootVal = preorder[startPreorderIndex];
TreeNode root = new TreeNode(rootVal);
if(startPreorderIndex == endPreorderIndex)
return root;
// 在中序遍历序列中找到当前节点的值
int rootIndex = startInorderIndex;
while(rootIndex < endInorderIndex && inorder[rootIndex] != rootVal)
rootIndex++;
if(rootIndex == endInorderIndex && inorder[rootIndex] != rootVal)
return null;
// 记录左子树的长度
int leftLength = rootIndex - startInorderIndex;
// 记录先序遍历中的终点
int leftPreorderIndex = startPreorderIndex + leftLength;
// 构建左子树, 取出先序遍历中与左子树等长的序列,取出中序遍历中被root分割的左半部分
if(leftLength > 0){
root.left = buildTree(preorder, inorder, startPreorderIndex + 1, leftPreorderIndex,
startInorderIndex, rootIndex - 1);
}
// 构建右子树,取出先序遍历序列除了左子树外剩余的部分,取出中序遍历中被root分割的右半部分
if(leftLength < endPreorderIndex - startPreorderIndex){
root.right = buildTree(preorder, inorder, leftPreorderIndex + 1, endPreorderIndex,
rootIndex + 1, endInorderIndex);
}
return root;
}
}
算法复杂度:
- 时间复杂度: O ( n 2 ) O(n^2) O(n2),需要建立 O ( n ) O(n) O(n)个节点,每个节点都要对中序遍历序列进行搜索。搜索一次需要 O ( n ) O(n) O(n)
- 空间复杂度,最坏的情况下,树退化成链表,递归深度达到 O ( n ) O(n) O(n),占用 O ( N ) O(N) O(N)额外空间,最好的情况是满二叉树递归深度为 O ( l o g n ) O(logn) O(logn),需要 O ( l o g n ) O(logn) O(logn)的空间。
利用哈希表查找的方法
class Solution {
int[] preorder;
Map<Integer, Integer> dic = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder == null || inorder == null || preorder.length == 0 || inorder.length == 0
|| preorder.length != inorder.length)
return null;
this.preorder = preorder;
for(int i = 0; i < inorder.length; i++){
dic.put(inorder[i], i);
}
return buildTree(0, 0, preorder.length - 1);
}
// root用于记录当前访问的先序数组的下标,left和right分别记录后序遍历序列的开始和结束下标
private TreeNode buildTree(int root, int left, int right){
if(left > right)
return null;
TreeNode node = new TreeNode(preorder[root]);
int rootIndex = dic.get(preorder[root]);
node.left = buildTree(root + 1, left, rootIndex - 1);
node.right = buildTree(root + 1 + rootIndex - left, rootIndex + 1, right);
return node;
}
}
算法复杂度
- 时间复杂度:需要建立 O ( n ) O(n) O(n)个节点,需要遍历后序序列,复杂度均为 O ( n ) O(n) O(n)
- 空间复杂度:建立哈希表需要 O ( n ) O(n) O(n)的空间;最坏的情况下,树退化成链表,递归深度达到 O ( n ) O(n) O(n),占用 O ( n ) O(n) O(n)额外空间,最好的情况是满二叉树递归深度为 O ( l o g n ) O(logn) O(logn),需要 O ( l o g n ) O(logn) O(logn)的空间。
有大佬写出了迭代的算法。题解链接,回头分析分析
/**
* 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 == null || preorder.length == 0) {
return null;
}
int length = preorder.length;
TreeNode root = new TreeNode(preorder[0]);
Stack<TreeNode> stack = new Stack<>();
stack.push(root);/*将根节点压栈*/
int inIndex = 0;
for (int i = 1; i < length; i++) {
TreeNode node = stack.peek();/*取出栈顶的结点*/
int currPreVal = preorder[i];/*暂时存放下个节点的值*/
if (node.val != inorder[inIndex]){/*寻找根节点,如果不等于,说明仍处于根节点的左子树中*/
TreeNode left = new TreeNode(currPreVal);
node.left = left;
stack.push(left);
}else {
while (!stack.isEmpty() && stack.peek().val==inorder[inIndex]){/*当前树弹出左子树和根节点*/
node = stack.pop();
inIndex++;
}
TreeNode right = new TreeNode(currPreVal);
node.right = right;
stack.push(right);
}
}
return root;
}
}
C++递归实现
/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
return rebuild(pre, vin, 0, pre.size() - 1, 0, vin.size() - 1);
}
TreeNode* rebuild(vector<int>& pre, vector<int>& vin, int pre_left,
int pre_right, int vin_left, int vin_right){
if(pre_left > pre_right)
return nullptr;
TreeNode* root = new TreeNode(pre[pre_left]);
for(int i = vin_left; i <= vin_right; i++){
if(vin[i] == root->val){
root->left = rebuild(pre, vin, pre_left + 1, pre_left + i - vin_left, vin_left, i - 1);
root->right = rebuild(pre, vin, pre_left + i - vin_left + 1, pre_right, i + 1, vin_right);
break;
}
}
return root;
}
};