必须选中间元素做根节点,因为只有这样左右子树节点数差≤1,才能保证高度差不超过1、避免退化为链表;若选端点则子树规模严重失衡,高度达o(n)。

为什么必须选中间元素做根节点
平衡二叉搜索树(BST)要求左右子树高度差不超过 1,而有序数组本身已按升序排列——这正好对应 BST 的中序遍历结果。要让树尽可能平衡,每次递归都必须取当前区间的 mid 作为根,否则左右子树节点数会严重失衡,最终高度可能退化为 O(n)。
常见错误是取左端点或随机索引,比如写成 root = new TreeNode(nums[left]),这样生成的树会变成向右倾斜的链表。
正确做法:用 (left + right) / 2 或更安全的 left + (right - left) / 2 防止整数溢出。
递归建树时边界怎么传才不越界
关键在闭区间还是左闭右开的选择。推荐统一使用 **左闭右闭**([left, right]),逻辑最直观,也和大多数二分查找习惯一致。
递归终止条件必须严格写成 if (left > right) return nullptr;,而不是 == 或漏判。否则容易触发 nums[right + 1] 越界访问,尤其当输入为空数组 nums = {} 时,初始调用 build(0, -1) 就会直接命中该条件并返回空指针。
左右子树递归调用时,区间要严格收缩:
- 左子树:
build(nums, left, mid - 1) - 右子树:
build(nums, mid + 1, right)
写成 mid 而不是 mid - 1 或 mid + 1 作子树边界,是初学者最常踩的坑。
C++ 实现里 vector& 为什么要传引用
如果不加 &,每次递归都会拷贝整个 vector,时间复杂度从 O(n) 暴涨到 O(n log n),空间也多出 O(n log n) 的临时副本。对长度 10⁵ 的数组,很可能直接超时或爆栈。
正确签名是:TreeNode* build(vector<int>& nums, int left, int right)</int> —— nums 必须是 const vector<int>&</int> 或至少 vector<int>&</int>;left 和 right 用值传递即可。
另外注意:不要试图用 vector::begin() + offset 切片再传新 vector,C++ 没有原生切片语法,那样反而更慢。
LeetCode 提交时遇到 stack overflow 怎么办
纯递归在极端情况下(如 2×10⁵ 个节点)可能导致系统栈溢出,尤其 MSVC 默认栈较小。这不是算法错,而是工程限制。
两个实际可用的解法:
- 改用迭代 + 显式栈模拟,维护
{left, right, &parent_ptr, is_left}元组,但代码变长且易错 - 更简单:确认编译器栈大小(如 g++ 可加
-Wl,--stack,33554432),但 OJ 通常不允许;所以首选优化递归深度——确保没写成尾递归缺失或重复计算
真正导致栈溢出的往往是递归终止条件写错,比如把 left > right 误写成 left >= right,导致 mid 计算后仍继续下钻,无限递归。
平衡 BST 构建本身是 O(n) 时间、O(log n) 栈空间(理想平衡下),只要边界和终止条件干净,10⁵ 规模完全没问题。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











