应利用bst性质剪枝递归中序遍历:仅当root->val>=low时访问左子树,仅当root->valhigh或空树并返回空vector,确保边界、溢出和类型一致性。

如何用递归中序遍历高效提取 BST 中的区间值
二叉搜索树(BST)天然支持有序遍历,中序遍历即得升序序列。若需提取 [low, high] 范围内的所有节点值,**不必遍历整棵树**——利用 BST 左val 时,左子树全小于 val,必然全小于 low,直接跳过左子树;同理,若 val > high,右子树全大于 val,必然全大于 high,跳过右子树。
实操建议:
- 写一个带引用参数的递归函数,如
void inorderInRange(TreeNode* root, int low, int high, vector<int>& res)</int>,避免频繁拷贝容器 - 递归前先判空:
if (!root) return; - 顺序很重要:先处理左子树 → 当前节点(仅当
low val )→ 再处理右子树;但注意,左/右子树是否进入取决于当前节点与边界的比较结果,不是无条件递归 - 别在节点值满足区间时才递归左右——那是错的。正确逻辑是:即使当前值在区间内,左子树仍可能含有效值(只要左子树最大值 ≥ low),所以仍要进左;但若
root->val ,左子树一定无效,跳过
迭代实现时如何避免栈爆掉或漏节点
用显式栈模拟中序遍历做区间查询,难点在于:不能像纯中序那样“一路压左”,因为提前退出或跳过分支会破坏标准流程。关键是要把「是否该访问左/右子树」的判断逻辑显式编码进入栈条件里。
实操建议:
- 栈中存
TreeNode*即可,不需要额外标记;但每次出栈后,不盲目压左右,而是按 BST 性质决策:
— 若root->val >= low,说明左子树可能含有效值,可压入root->left;
— 若root->val ,说明右子树可能含有效值,可压入 <code>root->right - 入栈顺序不影响结果,但推荐先压右再压左,这样出栈时左先被处理(保持中序倾向)
- 注意边界等于号必须包含:区间是闭区间
[low, high],所以判断用>=和,不是 <code>>或 - 极端情况:树深度很大但区间极窄(如只含一个值),迭代比递归更安全,避免系统栈溢出
返回结果时 vector 是传值还是传引用更合适
如果函数定义为 vector<int> rangeQuery(TreeNode*, int, int)</int> 并在内部构造 vector 返回,C++11 后通常触发移动语义,开销不大;但若调用方本就有一个目标容器(比如要追加到已有结果中),传引用更灵活、零拷贝。
实操建议:
- 对外接口优先用返回值方式,语义清晰、不易误用(比如不会意外清空旧数据)
- 内部实现或性能敏感路径(如高频查询)可用引用参数,但函数名应体现副作用,例如叫
collectInRange而非getRange - 别用全局或静态
vector缓存结果——多线程不安全,且下次调用前必须手动清空,极易出错 - 若需去重或排序后返回,注意 BST 中序本就是严格升序,区间内结果天然有序,无需额外
sort
遇到空树、low > high 或节点值溢出时怎么处理
这些不是边缘情况,而是真实生产中会触发的输入。例如用户传入 low = INT_MAX, high = INT_MIN,或树为空指针,都应有明确定义的行为,而不是崩溃或返回乱码。
实操建议:
- 函数开头加断言或防御性检查:
if (low > high) return {};—— 直接返回空vector,这是最合理语义 - 空树处理统一放在递归/迭代入口:
if (!root) return {};,不要分散在各处 - 节点值类型若为
int,比较时避免val + 1类运算以防溢出;区间判断始终用val >= low && val ,不要改写成 <code>!(val high)——虽然等价,但可读性和调试友好度差 - 如果业务要求支持
long long值域,节点结构体里的val类型和函数参数类型必须同步更新,否则隐式截断会导致查询错误
最容易被忽略的是:区间查询的「有效性」完全依赖 BST 性质成立。如果插入逻辑没维护好 BST 结构(比如重复插入未处理、删除后未修复),结果一定错——算法再对也没用。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











