力扣第105题 通过前中序列 构造二叉树 c++ 数据结构必学
题目
中等
相关标签
给定两个整数数组 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
保证 为二叉树的中序遍历序列
思路和解题方法
首先,定义了一个私有函数
traversal
,用于递归构建二叉树。函数的参数包括前序遍历结果数组、中序遍历结果数组以及它们的起始和终止位置。在函数内部,首先判断递归终止条件,即当前前序遍历区间只有一个元素时,直接创建节点并返回。
然后,通过前序遍历结果数组的第一个元素确定根节点的值,并创建根节点。
接下来,通过在中序遍历结果数组中查找根节点的值,确定中序遍历结果数组的切割点(即根节点的位置)。
将中序遍历结果数组切割成左右两个区间,分别对应左子树和右子树。
同时,根据中序遍历结果数组的切割点计算出前序遍历结果数组的左右两个区间。
然后,递归调用
traversal
函数分别构建左子树和右子树,并将其返回的结果分别赋值给根节点的左子节点和右子节点。最后,在主函数
buildTree
中判断输入的前序遍历和中序遍历数组是否为空,若为空则直接返回空指针。调用
traversal
函数,并传入前序遍历和中序遍历数组的起始和终止位置,得到构建好的二叉树,最后返回根节点。
复杂度
时间复杂度:
O(n)
时间复杂度为O(n),其中n是树中节点的数量。这是因为对于每个节点,需要遍历中序数组和前序数组来找到其在数组中的位置,而数组的总长度为n。
空间复杂度
O(n)
空间复杂度为O(n),其中n是树中节点的数量。这是因为在递归过程中,需要创建节点对象以及存储数组的切割区间,切割区间的总长度为n。
c++ 代码
class Solution { // 定义一个类名为Solution
private: // 定义类的私有部分
TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& preorder, int preorderBegin, int preorderEnd) { // 定义一个私有成员函数traversal,接收两个vector(前序遍历和中序遍历)和相应的开始和结束索引
if (preorderBegin == preorderEnd) return NULL; // 如果前序遍历的开始和结束索引相同,说明当前子树为空,返回NULL
int rootValue = preorder[preorderBegin]; // 取出前序遍历中开始位置的元素作为当前子树的根节点值
TreeNode* root = new TreeNode(rootValue); // 创建一个新的TreeNode对象,其值就是rootValue
if (preorderEnd - preorderBegin == 1) return root; // 如果前序遍历的开始和结束索引之差为1,说明当前子树只有一个节点,直接返回该节点
int delimiterIndex; // 定义一个变量用于保存分割点在中序遍历中的索引
for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) { // 从中序遍历的开始位置遍历到结束位置
if (inorder[delimiterIndex] == rootValue) break; // 如果找到与根节点值相同的元素,结束循环
}
// 在中序遍历中找到根节点对应的分割点,将中序遍历分为左右两部分
// 中序左区间,左闭右开[leftInorderBegin, leftInorderEnd)
int leftInorderBegin = inorderBegin; // 左区间的开始位置设为开始位置
int leftInorderEnd = delimiterIndex; // 左区间的结束位置设为分割点位置
// 中序右区间,左闭右开[rightInorderBegin, rightInorderEnd)
int rightInorderBegin = delimiterIndex + 1; // 右区间的开始位置设为分割点位置+1
int rightInorderEnd = inorderEnd; // 右区间的结束位置设为结束位置
// 在前序遍历中按照中序的分割点也进行分割
// 前序左区间,左闭右开[leftPreorderBegin, leftPreorderEnd)
int leftPreorderBegin = preorderBegin + 1; // 前序左区间的开始位置设为前序开始位置+1(因为根节点已经取走了)
int leftPreorderEnd = preorderBegin + 1 + delimiterIndex - inorderBegin; // 前序左区间的结束位置为前序开始位置+1+中序左区间大小(左闭右开)
// 前序右区间, 左闭右开[rightPreorderBegin, rightPreorderEnd)
int rightPreorderBegin = preorderBegin + 1 + delimiterIndex - inorderBegin; // 前序右区间的开始位置为前序开始位置+1+中序左区间大小
int rightPreorderEnd = preorderEnd; // 前序右区间的结束位置为前序的结束位置
root->left = traversal(inorder, leftInorderBegin, leftInorderEnd, preorder, leftPreorderBegin, leftPreorderEnd); // 递归处理左子树
root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, preorder, rightPreorderBegin, rightPreorderEnd); // 递归处理右子树
return root; // 返回处理完成的根节点
}
public: // 定义类的公有部分
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { // 定义公有函数buildTree,接收两个vector(前序遍历和中序遍历)作为参数
if (inorder.size() == 0 || preorder.size() == 0) return NULL; // 如果中序遍历或前序遍历为空,则返回NULL
// 参数坚持左闭右开的原则
return traversal(inorder, 0, inorder.size(), preorder, 0, preorder.size()); // 调用私有函数traversal处理数据并返回处理完成的根节点
}
};
觉得有用的话可以点点赞,支持一下。
如果愿意的话关注一下。会对你有更多的帮助。
每天都会不定时更新哦 >人< 。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)