BST最大值总在最右节点,因其左子树值均小于根、右子树值均大于根,故从根持续向右即得全局最大;最小值同理在最左节点;推荐迭代而非递归以避免栈溢出,且需空指针检查。

BST最大值为什么总在最右节点
因为二叉搜索树的定义决定了左子树所有节点值 val 小于根,右子树所有节点值大于根。所以从根出发一直往右走,每一步都选更大的分支,最终停在无法再向右的位置——那就是全局最大值。
实际写法就是循环遍历右指针,不递归更安全(避免栈溢出),也更快:
int findMax(TreeNode* root) {
if (!root) return 0; // 或抛异常,视业务而定
while (root->right) {
root = root->right;
}
return root->val;
}- 空树必须提前判断,否则
root->right会崩溃 - 不要用递归
findMax(root->right),虽然逻辑简洁,但深度大时有栈风险 - 返回值类型需与
TreeNode::val一致,常见是int,但若树存long long或自定义类型,这里要同步改
最小值同理:最左节点即答案
对称地,最小值一定落在最左端。路径上每次选左子节点,直到 left 为空。
int findMin(TreeNode* root) {
if (!root) return 0;
while (root->left) {
root = root->left;
}
return root->val;
}- 和
findMax共享相同健壮性要求:空指针检查不能省 - 若树允许重复值且你关心“最左出现位置”,这个逻辑仍成立;但若要找“第一个插入的最小值”,就得额外维护插入序,单纯 BST 结构不保证
- 时间复杂度稳定为
O(h),h是树高——平衡时是O(log n),退化成链表时变成O(n)
迭代 vs 递归:为什么推荐迭代写法
递归版本看着短:
立即学习“C++免费学习笔记(深入)”;
int findMax(TreeNode* root) {
if (!root || !root->right) return root->val;
return findMax(root->right);
}但它隐含两个问题:
- 每次调用压栈,深度 10⁵ 的树大概率触发
stack overflow - 编译器未必能做尾递归优化(尤其 MSVC 对非 trivial 类型常禁用),实际仍是递归调用
- 迭代版本更容易加日志、断点调试,比如想观察每步
root地址变化
除非你明确控制树深(例如红黑树封装内部,高度上限已知),否则生产代码优先用迭代。
带空指针安全的泛型封装建议
如果项目里多个地方要查极值,别反复写 while (root->left)。用模板+引用参数减少拷贝,同时处理空指针:
template<typename T>
bool findMin(TreeNode<T>* root, T& out) {
if (!root) return false;
while (root->left) root = root->left;
out = root->val;
return true;
}- 返回
bool表示是否成功找到,比抛异常或 magic value(如INT_MIN)更可控 - 注意
T必须支持赋值,且TreeNode<t>::val</t>类型要匹配 - 如果树节点用智能指针(如
std::unique_ptr<treenode></treenode>),那root->left写法不变,但传参要用const std::unique_ptr<treenode>>&</treenode>
最常被忽略的是:BST 的“最大/最小”只在整棵树结构有效时成立。如果有人手动修改了某个节点的 val 而没重平衡,这两个函数仍会返回物理最右/最左节点的值——但它未必是逻辑上的最大值。定位极值快,不代表数据本身可信。


















