bst删除节点需按子树结构分三类处理:无子节点则删后返nullptr;仅一子树则返该子节点以重连父链;双子树则用右子树最小节点(或左子树最大节点)替换,并摘除原节点,确保bst性质与指针自洽。

删除 BST 节点不能只靠“找到就删”,关键在于被删节点的子树结构——它有没有左子树、右子树、两个都有,直接决定你该用哪个子节点去顶替它。
为什么 deleteNode 必须返回 TreeNode*
因为 C++ 没有引用型指针(TreeNode&* 不合法),父节点的 left 或 right 成员必须被显式重赋值。如果你写成 void deleteNode(TreeNode* root, int key),那在递归中修改了子树根,父层根本看不到变化。
- 递归调用后必须把返回值重新挂到父节点上:
root->left = deleteNode(root->left, key); - 叶子节点删除后要返回
nullptr,否则父节点还指着已释放内存 - 单子树节点删除后,必须返回那个唯一子节点,才能接回父链
三种情况的替换逻辑与实现要点
被删节点 node 的子树组合决定了你“拿谁来填坑”:
-
无子节点(叶子):直接
delete node,返回nullptr -
仅左子树或仅右子树:返回非空子节点(
node->left或node->right),让父节点跳过它直接连子树 -
左右子树都存在:不能随便选一个子节点顶替——BST 性质会破。标准做法是找
node->left中的最大值(即左子树最右节点),或node->right中的最小值(即右子树最左节点)。选后者更常见,代码也更对称。
注意:替换不是“复制值”,而是物理移动指针。比如用右子树最小节点 successor 替代,要先摘下它(可能要处理它的右子树),再把 node 的左右子树分别挂到 successor 上,最后返回 successor。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
findMin 和 deleteMin 是否需要单独封装?
可以不封装,但强烈建议拆出来——否则主函数里会出现嵌套三层指针操作(如 node->right->left->left),极易出错且难调试。
-
findMin(TreeNode* root):一路向左,直到root->left == nullptr,返回root -
deleteMin(TreeNode* root):如果root->left == nullptr,返回root->right;否则递归处理root->left并更新root->left,最后返回root - 在双子树删除分支中,先调
auto successor = findMin(node->right);,再用node->right = deleteMin(node->right);把 successor 摘掉,最后拼接:successor->left = node->left; successor->right = node->right;
容易被忽略的内存与边界问题
新手常在这里翻车:
- 没判空就访问
root->left->val,触发段错误 —— 所有子节点访问前必须加if (root && root->left) - 用
new TreeNode(successor->val)复制值代替指针搬运,导致内存泄漏 + 重复释放(原successor还在树里) - 删除后忘记
delete node,尤其在双子树场景,node已被移出树但仍占内存 - 递归基写成
if (!root) return nullptr;是对的,但有人误写成if (root->val == key) {...}却没包if (root),一上来就解引用空指针
真正的难点不在“怎么删”,而在于“删完怎么让整棵树仍满足 BST 性质且指针关系自洽”。每一步指针重连,都要同步考虑父子双向关系和内存生命周期。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










