将有序数组转换为二叉搜索树
876 字
4 分钟
将有序数组转换为二叉搜索树
题目链接:108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode)
- 难度:简单
- 标签:树、二叉搜索树、分治、二叉树
什么是二叉搜索树 (BST)?
Note
定义:
- 左子树的所有节点值都小于根节点。
- 右子树的所有节点值都大于根节点。
- 左右子树本身也必须是二叉搜索树。
graph TD
8((8)) --- 3((3))
8((8)) --- 10((10))
3((3)) --- 1((1))
3((3)) --- 6((6))
10((10)) --- null1[null]
10((10)) --- 14((14))
style null1 fill:none,stroke:none
题目描述
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。
示例展示(多种合法的 BST)
方案 A:以 0 为根节点
graph TD
0((0)) --- n3(("-3"))
0((0)) --- 9((9))
n3(("-3")) --- n10(("-10"))
n3(("-3")) --- null1[null]
9((9)) --- 5((5))
9((9)) --- null2[null]
style null1 fill:none,stroke:none
style null2 fill:none,stroke:none
方案 B:另一种平衡形态
graph TD
0((0)) --- n10(("-10"))
0((0)) --- 5((5))
n10(("-10")) --- null3[null]
n10(("-10")) --- n3(("-3"))
5((5)) --- null4[null]
5((5)) --- 9((9))
style null3 fill:none,stroke:none
style null4 fill:none,stroke:none
示例 2
graph TD
3((3)) --- 1((1))
3((3)) --- null5[null]
style null5 fill:none,stroke:none
方案一:递归分治(最直观)
核心思路: 其实还是很好搞的。由于数组已经是排好序的,这就像是二分查找的逆过程:
- 找到数组的中间节点作为根。
- 之后左右分两条路,分别递归构建左右子树。 这样能保证左右两边的节点数差值不超过 1,天然就是平衡的!
源码实现
class Solution {public: TreeNode* sortedArrayToBST(vector<int>& nums) { // 主函数:从整个数组区间开始构建 return build(nums, 0, nums.size() - 1); }private: TreeNode* build(const vector<int>& nums, int left, int right){ // 递归终止条件:左指针超过右指针 if(left > right) return nullptr;
// 找到中间位置,避免 (left + right) / 2 可能带来的溢出风险 int mid = left + (right - left) / 2;
// 创建当前子树的根节点 TreeNode* root = new TreeNode(nums[mid]);
// 递归地构建左右子树 root->left = build(nums, left, mid - 1); // 左半部分去盖左楼 root->right = build(nums, mid + 1, right); // 右半部分去盖右楼
return root; }};复杂度分析
- 时间复杂度:。每个数组元素都会被访问并转换为节点。
- 空间复杂度:。递归栈的深度等于平衡树的高度。
方案二:迭代法(手动压栈)
如果不想用递归,也可以用栈来模拟这个过程。我们定义一个 Task 结构,专门记录“我们要用哪段区间盖哪棵子树”。
源码实现
class Solution {public: struct Task { int l, r; TreeNode** parentPtr; // 指向父亲节点中 left 或 right 指针的地址 Task(int l_, int r_, TreeNode** p_) : l(l_), r(r_), parentPtr(p_) {} };
TreeNode* sortedArrayToBST(vector<int>& nums) { if (nums.empty()) return nullptr; TreeNode* root = nullptr; stack<Task> st; st.emplace(0, (int)nums.size() - 1, &root);
while (!st.empty()) { auto [l, r, ptr] = st.top(); st.pop(); if (l > r) { *ptr = nullptr; continue; }
int mid = l + (r - l) / 2; *ptr = new TreeNode(nums[mid]); // 直接给对应的指针赋值
// 下一次要处理的任务,先压右后压左 st.emplace(mid + 1, r, &(*ptr)->right); st.emplace(l, mid - 1, &(*ptr)->left); } return root; }};总结
- 有序即平衡:在有序数组里取中点,是构建平衡 BST 的“金科玉律”。
- 递归与二分的联动:你会发现这道题的代码逻辑和二分查找惊人地相似,只是二分是在找值,而我们在造树。
Tip
为什么一定要取中点?因为只有中点能让左右两边的“劳动力”(节点数)分配得最均匀,从而让整棵树长得最匀称!
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
最后更新于 2025-11-10,距今已过 170 天
部分内容可能已过时
相关文章 智能推荐
1
对称二叉树
算法 力扣第101题:对称二叉树。深入理解树的镜像对称性质,掌握递归与迭代两种姿势下的双向比对技巧。
2
二叉树的最大深度
算法 力扣第104题:二叉树的最大深度。深入理解递归与广度优先搜索(BFS)在计算树结构深度时的应用场景。
3
二叉树的中序遍历
算法 力扣第94题:二叉树的中序遍历。从递归的简洁到迭代的深度剖析,掌握树结构遍历的核心逻辑。
4
合并两个有序数组
算法 力扣第88题:合并两个有序数组。从正向合并的繁琐到逆向双指针的极简之美,实现 $O(m+n)$ 的高效原地合并。
5
删除有序数组中的重复项
算法 力扣第26题:原地删除有序数组中的重复项。从基础的标记替换,到快慢指针(Two Pointers)的高效演进。
随机文章 随机推荐