问题
给定两个整数数组 preorder
和 inorder
,其中 preorder
是二叉树的先序遍历, inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例 1:
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] 输出: [3,9,20,null,null,15,7]
示例 2:
输入: preorder = [-1], inorder = [-1] 输出: [-1]
提示:
1 <= preorder.length <= 3000
inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder
和inorder
均 无重复 元素inorder
均出现在preorder
preorder
保证 为二叉树的前序遍历序列inorder
保证 为二叉树的中序遍历序列
解析
从二叉树的性质可以知道,先序可以确定跟,中序可以根据根的位置分割左子树和右子树。
根据这个性质及区间的划分来构建一棵二叉树。
二叉树的经验
1.往往需要递归解决问题
2.存在区间的递归往往需要借助区间来完成递归条件的返回。(控制返回语句)
代码
TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder,
int& prei, int inbegin, int inend) //保证prei每次都是相同的prei
{
//返回
if (inbegin > inend) //等于表示还有一个
return nullptr;
//去中序遍历找根
int rooti = 0;
while (inbegin <= inend)
{
if (inorder[rooti] == preorder[prei]) //preorder[previ]为根
break;
rooti++; //保证跳出循环时rooti的下标就是根的下标,而不是再++
}
//先序确定根
TreeNode* newnode = new TreeNode(preorder[prei++]);
//中序分割左右子树的区间
//分割成了 [inbegin, rooti - 1] rooti [rooti + 1, inend];
newnode->left = _buildTree(preorder, inorder, prei, inbegin, rooti - 1);
newnode->right = _buildTree(preorder, inorder, prei, rooti + 1, inend);
return newnode; //返回到上一层的递归处,完成链接(注意返回类型)
}
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
TreeNode* root = nullptr;
int prei = 0;
int inbegin = 0;
int inend = preorder.size() - 1;
root = _buildTree(preorder, inorder, prei, inbegin, inend);
return root;
}
};
从返回类型可以看出,需要构建一个二叉树,用一个root指针指向这个二叉树并返回。
需要注意的是prei需要引用传参,保证访问的是线序列表的同一个prei
代码解剖
1.递归返回
借助区间完成
//返回
if (inbegin > inend) //等于表示还有一个
return nullptr;
2.借助先序根的位置去中序找到根,以完成区间的划分
//去中序遍历找根
int rooti = 0;
while (inbegin <= inend)
{
if (inorder[rooti] == preorder[prei]) //preorder[previ]为根
break;
rooti++; //保证跳出循环时rooti的下标就是根的下标,而不是再++
}
3.先序确定根
//先序确定根
TreeNode* newnode = new TreeNode(preorder[prei++]);
4.中序划分区间,分割成了 [inbegin, rooti - 1] rooti [rooti + 1, inend];
//中序分割左右子树的区间
//分割成了 [inbegin, rooti - 1] rooti [rooti + 1, inend];
newnode->left = _buildTree(preorder, inorder, prei, inbegin, rooti - 1);
newnode->right = _buildTree(preorder, inorder, prei, rooti + 1, inend);
5.返回 最后的返回一定不能忘!
return newnode; //返回到上一层的递归处,完成链接(注意返回类型)
为什么能完成链接呢?
就是因为在连接处进行递归调用,同时下一层递归调用中返回了root指针。从而在连接处层层递归调用,再返回到链接处。
整体的连接逻辑可以理解为:新建指针、链接处递归、下一层递归函数、新建指针、执行完函数中间部分的语句、返回指针到上一层的链接部分的递归调用处。以此完成链接。