双旋转用于解决单旋转无法恢复平衡的情况:当节点平衡因子为±2且失衡子树向相反方向倾斜时,需先对子节点单旋“扶正”,再对原节点单旋;lr/rl旋转顺序不可颠倒,且每次单旋后须立即更新涉及节点高度。

LL/RR 单旋转不够用时,必须触发 LR/RL 双旋转
当插入或删除导致某节点平衡因子变为 ±2,且其失衡子树本身也朝相反方向倾斜,单旋转无法恢复高度平衡。比如左子节点右倾(node->left->right 比 node->left->left 高),此时对 node 做 LL 旋转仍会残留不平衡——必须先对左子节点做 RR 旋转“扶正”它,再对原节点做 LL 旋转。RL 同理。
关键判断逻辑在 getBalanceFactor() 和子树高度差组合:只有当 balance(node) == 2 && balance(node->left) == -1 时走 LR;balance(node) == -2 && balance(node->right) == 1 时走 RL。
- 双旋转本质是两次单旋转的组合,不是新操作,但顺序不可颠倒
-
LR= 先rotateRight(node->left),再rotateLeft(node) -
RL= 先rotateLeft(node->right),再rotateRight(node) - 旋转后必须重新更新涉及节点的高度(
updateHeight()),否则后续getBalanceFactor()计算错误
AVL 插入后自底向上回溯时,双旋转只发生在首个失衡点
插入后从新节点向上逐层更新高度并检查平衡因子,一旦发现某个节点 abs(balance) > 1,立即执行对应旋转(LL/RR/LR/RL),然后**停止向上检查**——因为旋转已使该子树恢复平衡,且高度可能减 1,但其父节点的高度和平衡性需由本次旋转后的返回值重新计算,不能跳过。
常见错误是继续遍历到根,或在旋转后未正确返回新子树根,导致指针断裂:
- 每次旋转函数(如
rotateLeft())必须return新的子树根节点 - 插入递归返回时,要将旋转结果赋给对应子指针:
root->left = insert(root->left, val),而非忽略返回值 - 若使用非递归插入,需维护父节点栈,并在回溯时用栈顶节点接收旋转后的新子树根
RL 旋转中容易漏掉中间节点的高度更新
以 node 失衡、node->right 右倾、node->right->left 为高为例:RL 旋转分两步——先对 node->right 左旋,再对 node 右旋。但第一次左旋后,node->right 的子节点结构已变,其高度必须立刻更新;第二次右旋前,node 的左右子树高度也已变化,不更新会导致最终 node 高度错误。
正确做法是:每次单旋转函数内部,在调整指针后立即调用 updateHeight() 更新被旋转的两个核心节点(如 rotateLeft() 中更新 y 再更新 x);双旋转函数中,两次单旋之间不额外更新,靠单旋内部保证。
- 别在双旋转函数末尾统一更新——中间状态的高度是错的
-
updateHeight()必须用max(leftHeight, rightHeight) + 1,不能直接加 1 或抄旧值 - 空节点高度按 -1 处理(标准 AVL 定义),否则
balance = leftHeight - rightHeight计算偏移
调试时重点抓三个输出点:插入路径、各节点 balance、旋转前后指针关系
双旋转逻辑绕,光看最终树形很难定位是判断条件错、旋转顺序错,还是高度没更新。建议在关键位置加日志:
- 插入时打印路径:
cout - 每次更新高度后立刻输出:
cout - 执行旋转前打印子树结构:
printSubtree(node, 0)(简单缩进打印) - 特别注意旋转后
node的父指针是否还指向它——如果用局部变量交换而没改上级引用,树就断了
最常被忽略的是:旋转操作修改了节点指针关系,但调用方没用返回值覆盖原指针。例如写成 rotateRight(node->right); 而不是 node->right = rotateRight(node->right);,整棵子树直接丢失。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











