直接递归找max/min只需沿最右/最左路径,因BST性质保证极值在末端叶子;时间复杂度O(h),需处理空树边界,推荐迭代实现避免栈溢出。

为什么直接递归找 max 和 min 要往最右/最左走,而不是遍历整棵树?
因为二叉搜索树(BST)的定义决定了:任意节点的左子树所有值都小于它,右子树所有值都大于它。所以最大值一定在最右侧叶子节点,最小值一定在最左侧叶子节点——不需要比较中间任何值,更不需中序遍历全树。
-
min就是不断调用left指针,直到left == nullptr的节点 -
max就是不断调用right指针,直到right == nullptr的节点 - 时间复杂度稳定为
O(h)(h 为树高),最坏情况O(n)(退化为链表),但远优于O(n)的全遍历
递归实现 findMin 和 findMax 的边界处理要点
递归函数必须处理空树情况,否则一上来解引用 nullptr 就崩溃。同时,递归终止条件不是“叶子节点”,而是“无左/右子节点”——因为 BST 的极值只依赖单向路径。
- 空树时,
findMin应返回nullptr(或抛异常,取决于接口契约),不能返回任意默认值 -
findMin(root)的递归逻辑是:root->left ? findMin(root->left) : root -
findMax(root)同理:root->right ? findMax(root->right) : root - 不要写成
if (!root || !root->left) return root;—— 这会在root非空但left为空时提前返回,是对的;但若误写成!root->left && !root->right,就错判为叶子才停,会漏掉单边延伸的极值节点
const 正确性和返回类型怎么选?
查找操作不应修改树结构,所以参数应为 const Node*;但返回值是否加 const 取决于后续用途。如果只是读取值(如 node->val),返回 const Node* 更安全;如果后续要删该节点,则需返回非 const 指针。
- 推荐接口签名:
const Node* findMin(const Node* root)和Node* findMin(Node* root)(重载,适配非常量上下文) - 避免返回局部变量或临时对象的地址——这里全是树内已有节点,没问题
- 不要返回
Node&:万一输入是nullptr,引用无法绑定,不如指针明确可空
迭代写法其实更简洁,什么情况下必须用递归?
纯递归没有栈溢出风险时(树深可控),代码更贴近 BST 的数学定义;但生产环境建议优先迭代——它显式、无递归开销、不会因深度过大而 stack overflow。
立即学习“C++免费学习笔记(深入)”;
- 递归版易读,适合教学或深度较浅的嵌入式 BST(如红黑树局部查找)
- 迭代版只需两行:
while (root->left) root = root->left;,等价且更鲁棒 - 若 BST 节点带父指针,
min/max还能进一步优化为O(1)维护(但需额外空间和更新逻辑)
真正容易被忽略的是:极值节点的“高效”不来自算法多巧妙,而来自对 BST 性质的严格依赖——一旦树不是 BST(比如插入时没维护有序性),这套方法立刻失效,返回结果完全不可信。


















