bst区间查询的正确语义是利用中序有序性和递归剪枝,仅访问与区间[l,r]有交集的子树:若子树max_val r则跳过右子树,否则递归查询并聚合sum、count等信息,时间复杂度平均o(log n)。

什么是BST区间查询的正确语义
二叉搜索树(BST)的区间查询不是简单地遍历所有节点再过滤,而是利用左小右大的结构性质跳过无关子树。如果你用 inorderTraversal 全量遍历后再 filter,时间复杂度就是 O(n),完全浪费了 BST 的优势。
真正的区间查询应只访问可能落在 [low, high] 内的节点:当当前节点值 val ,左子树全小于 <code>low,直接剪掉,只递归右子树;当 val > high,右子树全大于 high,只递归左子树;否则左右都查。
递归实现中容易漏掉的边界处理
很多人写递归时只判断 val >= low && val 就加入结果,但忽略:即使当前节点不在区间内,它的某棵子树仍可能有合法节点。比如 <code>val = 5,low = 3,high = 7,此时左右子树都要查;但若 val = 2,low = 3,那左子树全 ≤ 2,不可能满足 ≥ 3,必须跳过左子树。
- 必须对每个节点做三向分支判断:
val → 只走右;<code>val > high→ 只走左;否则左右都走 - 插入结果的时机是
val >= low && val ,不是“进入该节点时就加” - 注意
low和high是否包含端点——BST 本身不保证重复值,但若允许重复,需明确是否用还是 <code>
迭代写法如何避免栈爆或逻辑错乱
用栈模拟递归时,不能只压入节点指针,必须同时记录该节点对应的有效搜索范围(即它在原BST中被约束的上下界),否则无法做剪枝。例如根节点范围是 [-INF, +INF],进入左子节点后,其合法范围变成 [-INF, root->val);进右子节点则变成 (root->val, +INF]。
常见错误是只存节点,然后靠节点值现场判断——这会退化成 DFS 全遍历。
- 栈元素类型建议为
std::tuple<treenode int></treenode>(节点指针、当前子树最小允许值、最大允许值) - 初始压入
{root, INT_MIN, INT_MAX},注意INT_MIN/INT_MAX可能和节点值冲突,更安全用long long或std::optional<int></int>表示无界 - 每次出栈后,先检查
val是否在当前区间内,再决定是否将左右子节点按新约束压栈
返回结果时的性能与内存选择
如果只是统计数量,用 int 计数即可;如果要提取所有节点值,返回 std::vector<int></int> 最常用。但要注意:BST 区间内节点数可能远小于总节点数,提前 reserve 容量没意义;而频繁 push_back 的摊还成本可接受。
若调用方后续只读、且数据量大,考虑返回 std::vector<int>&&</int> 避免拷贝;但别返回局部容器的引用——这是悬垂引用。
- 不要用
std::list替代vector:BST 中序天然有序,vector支持随机访问且 cache 友好 - 如果上层需要迭代器接口(如支持 range-for),可封装一个轻量迭代器类,但多数场景直接返回 vector 更直白
- 若区间极大(接近全树),递归深度可能触发栈溢出——此时强制切换到迭代写法,而非调大栈空间
low 是 long long,但节点值是 int,比较时隐式转换可能掩盖溢出;或者 low 为 nullptr 表示无下界,但代码里写成 if (low == nullptr) 却忘了初始化指针——这类细节不报编译错,但一跑就漏数据。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











