LL/RR旋转触发条件为节点平衡因子绝对值超1且失衡路径呈直线型:LL需左子节点平衡因子≥0,RR需右子节点≤0;代码边界须在旋转入口判空,更新高度时先子节点后新根,且旋转后必须返回新根并由上层赋值。

LL/RR 旋转的触发条件和代码边界怎么写才不越界
LL 和 RR 是最基础的单旋,但很多人在 height 更新和空指针判断上栽跟头。关键不是“要不要判空”,而是“在哪一层判”——必须在旋转函数入口就检查子节点是否为空,否则 node->left->right 这类链式访问直接崩溃。
常见错误现象:segmentation fault 发生在计算平衡因子前,因为 node->left 已为 nullptr,还去取它的 height。
- 所有涉及子节点成员访问(如
left->height、right->height)前,先用if (!node || !node->left)检查 -
getBalanceFactor(node)函数内部要容忍nullptr:返回!node ? 0 : node->height的差值 - LL 旋转后,原左子节点的新右子树来自旧根的左子树(即
newRoot->right = oldRoot->left),不是oldRoot->right—— 这个方向错会导致结构断裂
LR/RL 旋转为什么必须拆成两步,不能直接重连
LR 不是“先 L 再 R”的简单拼接,而是对左子树做 RR 旋转、再对整棵树做 LL 旋转;RL 同理。跳过中间步骤会导致高度信息错乱、平衡因子失效,甚至让树退化回普通 BST。
使用场景:当插入导致 node->left->right 过高(LR)时,只做 LL 会把 node->left->right 整体压到左边,反而加剧失衡。
立即学习“C++免费学习笔记(深入)”;
- LR 旋转必须先调用
rightRotate(node->left),再调用leftRotate(node);两步之间要重新计算中间节点的高度 - 不能省略中间节点的
updateHeight():比如 RR 旋转后,node->left的高度变了,不更新就直接 LL,新根的height就是错的 - RL 旋转中,第一次
leftRotate(node->right)后,node->right指针已指向新子树根,后续rightRotate(node)必须作用于这个新结构
insert 后递归回溯时,height 更新和旋转的顺序不能颠倒
AVL 插入是递归向下找位置,然后向上回溯修正。如果先更新 height 再判断是否旋转,就会用错误的高度算出错误的平衡因子;如果先旋转再更新,又可能漏掉旋转后子树的高度变化。
正确顺序是:回溯到每个节点时,先递归修正子树 → 再更新当前节点 height → 再计算平衡因子 → 最后按需旋转。
- 每次递归返回后,立刻执行
node->height = 1 + std::max(getHeight(node->left), getHeight(node->right)) - 旋转函数(如
leftRotate)内部也要更新涉及节点的height,且顺序是:先更新子节点,再更新新根 —— 因为新根高度依赖子树高度 - 旋转后必须返回新根指针,并由上层递归赋值给对应左/右指针(如
root->left = leftRotate(root->left)),否则旋转无效
为什么 delete 场景下 RL/RR 旋转比 insert 更容易出逻辑漏洞
删除节点后,失衡点往往不在被删节点路径上,而在其祖先的“另一侧”子树。例如删掉左子树里一个节点,可能导致右子树相对过高,触发 RL 或 RR —— 这和插入的局部性不同,需要更谨慎地复用旋转逻辑。
性能影响:delete 后的平衡操作平均比 insert 多一次旋转判断,因为删除可能引发多层连续失衡(尤其在密集删除后)。
- delete 中的旋转调用必须带返回值赋值,比如
root = rightRotate(root),否则旋转结果丢失 - RL 旋转在 delete 中常出现在“右子树的左子树被删矮了”,此时
root->right的平衡因子为 -2,而root->right->left的平衡因子为 +1 —— 这个组合必须走 RL,不能误判为 RR - 所有旋转函数应声明为
Node* leftRotate(Node* node)而非void,避免因 C++ 值传递导致指针未更新
真正卡住人的从来不是旋转方向的记忆,而是 height 更新时机、空指针防御位置、以及旋转后指针是否真的被上层接收——这三处任意一处松动,整棵树就在静默中失去 AVL 性质。


















