平衡因子是节点左右子树高度差,需动态更新而非静态存储;插入删除时须沿路径回溯重算,旋转仅影响局部节点balance,删除还需处理多级失衡与高度传递。

平衡因子是什么,为什么不能静态计算
平衡因子不是存下来就一劳永逸的值,它是节点左右子树高度差:balance = height(left) - height(right)。只要任意节点的子树结构变化(插入/删除),其祖先路径上所有节点的平衡因子都可能改变——所以必须动态更新,而不是每次需要时重新递归算高度(那样是 O(n) 的)。
常见错误是只在插入后调用一次 updateBalance(root),却没沿插入路径向上修正每个父节点;或者误以为旋转后只需改三个节点的 balance,漏掉更高层祖先。
插入时如何沿路径更新 balance
标准 BST 插入递归返回后,自然回溯到父节点。此时可立即重算当前节点的 balance,并根据变化决定是否继续向上传播。
- 插入后,从新节点位置开始向上回溯,对每个经过的节点执行:
node->balance = getHeight(node->left) - getHeight(node->right) - 但
getHeight()若每次都递归求高,会退化成 O(n);正确做法是让insert()返回子树新高度,父节点直接用左右返回值计算 balance 和自身新高度 - 更高效的是在节点中冗余存储
height字段(或仅存 balance,用height = max(left_height, right_height) + 1反推),避免重复遍历
AVL 旋转后 balance 怎么重设才不出错
四种旋转(LL、RR、LR、RL)各自有固定的 balance 更新模式,硬记容易错。关键是:旋转只影响参与旋转的 2~3 个节点,其余节点 balance 不变;且旋转前后整棵树高度最多变化 1,因此只需重设旋转涉及节点的 balance,无需再向上更新(除非你刚做完旋转又触发了更高层失衡)。
例如 LL 旋转(右旋)后:
A B
/ \ / \
B C → D A
/ \ / \
D E E C
只需重设 B 和 A 的 balance:B->balance = max(getHeight(B->left), getHeight(D)) - getHeight(A) → 实际简化为:B->balance = max(B->left ? B->left->balance : 0, 0) - (A->balance > 0 ? 1 : 0) ——但更稳妥的做法是直接按定义重算:B->balance = getHeight(B->left) - getHeight(B->right),同理更新 A->balance。
真正容易踩的坑是:旋转后没同步更新相关节点的 height 或 balance,导致后续插入判断失衡位置错误;或在 LR/RL 旋转中,先对子树做了一次旋转却忘了更新其根节点 balance,再做主旋转时用了过期值。
删除操作中 balance 更新比插入更麻烦
删除可能引发多级回溯失衡,且同一路径上可能连续触发多次旋转。这时候不能只靠“插入式”的单次向上更新——因为删掉一个叶子后,某祖先可能从 balance=0 变成 ±2,修复旋转后,它的父节点 balance 可能也越过阈值。
- 必须在删除递归返回时,对每个回溯节点重新计算 balance,并检查是否失衡(
abs(node->balance) > 1) - 一旦失衡,执行对应旋转,然后**重设旋转涉及节点的 balance**,再把旋转后的新子树高度返回给父节点
- 注意:旋转后整棵子树高度可能减 1,这个变化必须准确传回,否则父节点算出的 balance 仍是错的
实际编码中,建议统一用返回值传递子树高度(而非维护 height 字段),既减少内存占用,也规避字段不同步风险;但若频繁查询高度(如调试时打印),冗余 height 字段反而更直观。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











