力扣 1008 题解析与 C++ 代码
一、问题解析题目描述 :给定一个整数数组 preorder,表示二叉搜索树(BST)的前序遍历 结果,请重建该二叉搜索树并返回其根节点。题目保证对于给定的测试用例,总能找到一棵二叉搜索树与之对应。
核心要点:
- 二叉搜索树(BST)性质:对于任意节点,其左子树所有节点值 < 该节点值 < 右子树所有节点值。
- 前序遍历顺序:根节点 → 左子树 → 右子树。
- 重建思路:前序遍历的第一个元素是根节点,随后连续小于根节点的部分是左子树的前序遍历,剩余部分是右子树的前序遍历。递归应用此规则即可重建整棵树。
算法步骤:
- 取当前区间
[start, end]的第一个元素作为根节点值。 - 找到第一个大于根节点值的索引
mid,该索引将区间划分为左子树[start+1, mid-1]和右子树[mid, end]。 - 递归构建左子树和右子树。
- 返回根节点。
时间复杂度 :O(n²),最坏情况(如斜树)每次需线性扫描划分左右子树。可优化至 O(n)(见后续优化方案)。
空间复杂度:O(n),递归栈深度。
二、C++ 代码实现
基础递归解法
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* bstFromPreorder(vector<int>& preorder) {
return build(preorder, 0, preorder.size() - 1);
}
private:
TreeNode* build(vector<int>& preorder, int start, int end) {
if (start > end) return nullptr;
// 当前子树的根节点是前序遍历的第一个元素 TreeNode* root = new TreeNode(preorder[start]);
// 找到第一个大于根节点值的索引,划分左右子树 int mid = start + 1;
while (mid <= end && preorder[mid] < preorder[start]) {
mid++;
}
// 递归构建左右子树 root->left = build(preorder, start + 1, mid1);
root->right = build(preorder, mid, end);
return root;
}
};
优化解法(O(n) 时间复杂度)
利用单调栈模拟递归过程,一次遍历完成构建:
cpp
class Solution {
public:
TreeNode* bstFromPreorder(vector<int>& preorder) {
if (preorder.empty()) return nullptr;
stack<TreeNode*> stk;
TreeNode* root = new TreeNode(preorder[0]);
stk.push(root);
for (int i = 1; i < preorder.size(); i++) {
TreeNode* node = new TreeNode(preorder[i]);
if (preorder[i] < stk.top()->val) {
// 当前值小于栈顶,作为左子节点
stk.top()->left = node;
} else {
// 当前值大于栈顶,找到合适的父节点 TreeNode* parent = nullptr;
while (!stk.empty() && preorder[i] > stk.top()->val) {
parent = stk.top();
stk.pop();
}
parent->right = node;
}
stk.push(node);
}
return root;
}
};
三、算法对比
| 特性 | 基础递归解法 | 优化栈解法 |
|---|---|---|
| 时间复杂度 | O(n²) | O(n) |
| 空间复杂度 | O(n) | O(n) |
| 核心思想 | 递归划分左右子树 | 单调栈维护节点关系 |
| 适用场景 | 数据量较小 | 数据量较大或要求高效 |
| 代码复杂度 | 简单直观 | 稍复杂但高效 |
四、关键点说明
- 边界处理 :递归解法中需确保
start > end时返回nullptr,避免无限递归。 - 划分点查找:基础解法中线性查找第一个大于根节点的索引,是 O(n²) 复杂度的根源。
- 单调栈原理:优化解法利用栈保存尚未设置右子树的节点,遇到更大值时弹出栈顶直到找到合适父节点,确保 BST 性质。
- 相关题目:本题与力扣 105(从前序与中序遍历构造二叉树)思路相似,但 BST 性质使得无需中序遍历即可确定左右子树边界。
五、测试示例
cpp
// 测试代码示例
int main() {
Solution solution;
vector<int> preorder = {8, 5, 1, 7, 10, 12};
TreeNode* root = solution.bstFromPreorder(preorder);
// 中序遍历验证(BST 中序遍历应为升序)
// 预期输出:1 5 7 8 10 12 return 0;
}