必须用ll旋转当插入导致右子树比左子树低2且失衡节点左子的左子树更高;rr需先更新p高度再更新q;lr/rl必须分两步旋转并自底向上更新高度;插入后须自底向上逐层检查平衡因子并重算高度。

LL旋转:什么时候必须用rotateLL?
当插入导致右子树高度比左子树低2,且失衡节点的左子节点的左子树更高(即balanceFactor ≥ 0)时,必须执行LL旋转。这不是“可选优化”,而是AVL定义下恢复平衡的刚性操作。
- 失衡节点记为
p,其左子节点为q - 旋转后:
q上移为新根,p变为q的右子,原q的右子(若有)成为p的左子 - 必须同步更新
p和q的height字段,否则后续判断会错——常见漏掉这步,导致插入后看似平衡、实则height值错误,下次插入直接崩溃 - 示例片段:
Node* rotateLL(Node* p) { Node* q = p->left; p->left = q->right; q->right = p; p->height = max(height(p->left), height(p->right)) + 1; q->height = max(height(q->left), height(q->right)) + 1; return q; }
RR旋转:和LL对称,但height更新顺序不能反
RR是LL的镜像,但新手常在更新height时误把父节点p的计算放在子节点q之前——这是错的。因为p->height依赖于q->right(即原p的右子),而该指针已被修改;必须先算p->height,再算q->height。
-
q是p的右子,旋转后q上位,p变左子 - 若
q原本有左子树,它会成为p的右子 —— 这个衔接点容易写反指针 - 不要复用LL的代码改符号了事;RR中
p->right = q->left和q->left = p的赋值方向必须严格对应拓扑
LR和RL旋转:为什么必须拆成两步?
LR不是“一次旋转”,而是先对p->left做RR,再对p做LL。强行合并逻辑会导致指针错乱,尤其当中间节点q(即p->left)本身有复杂子树时。
- 常见错误:试图用一个函数完成LR,结果
q->right被旋转两次或未更新 - 正确做法:先调用
rotateRR(p->left),再将返回值赋给p->left,最后调用rotateLL(p) - RL同理,先
rotateLL(p->right),再rotateRR(p) - 注意:两次旋转后,三个节点(
p、q、r)的height都需重算,且顺序是自底向上(先最深的子节点,再中间,最后根)
插入后平衡检查:别只看当前节点的balanceFactor
AVL插入不是插完就完,必须从插入点向上回溯到根,逐层检查并旋转。很多实现只检查父节点,漏掉祖父及以上节点可能仍失衡。
- 每次递归返回时,必须重新计算当前节点
height,再算balanceFactor = height(left) - height(right) - 若
abs(balanceFactor) > 1,才触发对应旋转;旋转后该子树高度可能变化,需继续向上反馈 - 别用
if (bf == 2 || bf == -2)代替abs(bf) > 1——虽然数值等价,但语义不清晰,易在扩展支持其他平衡策略时埋坑
真正难的不是写出四种旋转,而是让它们在插入/删除的递归路径中精准触发、高度正确更新、且不破坏已有指针关系。哪怕一个height字段滞后更新一帧,整棵树的平衡判定就会雪崩。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











