← 返回题目列表

AVL 树插入一个节点后如何恢复平衡?

高频 中等 第 6 / 25 题 更新于 2026/08/03
AVL树插入旋转平衡

简化版

先像普通 BST 一样把新节点插到叶子位置,然后从插入点沿路径回到根,逐个更新祖先的高度、检查平衡因子。遇到第一个失衡(|BF|=2)的节点,按 LL/RR/LR/RL 做对应旋转即可。关键结论:AVL 插入最多只需一次旋转(单旋或双旋)就能恢复整棵树的平衡,之后更上层的节点高度也会随之复原。

详细版

插入分三步:

  1. BST 插入:按值大小找到空位,新建节点挂上去(新节点初始高度为 1、平衡因子为 0)。
  2. 回溯更新高度:从新节点往上回到根,每经过一个祖先就重新计算它的高度 height = max(左高, 右高) + 1
  3. 检查并旋转:在回溯过程中遇到第一个 |BF|=2 的节点,判断失衡类型做旋转。
TreeNode insert(TreeNode node, int val) {
    if (node == null) return new TreeNode(val);
    if (val < node.val)      node.left  = insert(node.left,  val);
    else if (val > node.val) node.right = insert(node.right, val);
    else return node;                          // 值重复,不插

    update(node);                              // 更新高度
    return rebalance(node);                    // 检查平衡因子并按类型旋转
}

rebalance 里根据 BF 判断 LL/RR/LR/RL 并旋转。

完整版教学

一、为什么插入最多一次旋转

这是 AVL 插入最重要的结论。插入一个节点只会让「从插入点到根这条路径上」的节点高度可能 +1。当遇到第一个失衡的祖先并对它旋转后,旋转会把这棵子树的高度恢复到插入前的值——也就是说旋转后,这棵子树对更上层的高度贡献没有变化,上面的节点也就不再失衡了。所以一次旋转(单旋算一次、双旋也算一次调整)就够了,不需要继续往上调整。

这和删除形成鲜明对比:删除可能需要一路旋转到根(见 AVL 删除)。插入「一次搞定」是 AVL 的一个好性质。

二、回溯更新高度是关键

AVL 靠每个节点存的高度字段来算平衡因子,所以插入后必须自底向上更新沿途祖先的高度。用递归实现时,这一步天然发生在「递归返回的路上」——先递归插入子树,返回后更新当前节点高度、再检查平衡,正好是自底向上的顺序。

三、四种失衡的判定与处理

在某个失衡节点(|BF|=2)上,结合它和较高孩子的平衡因子判断类型:

TreeNode rebalance(TreeNode node) {
    int bf = bf(node);
    // 左偏
    if (bf > 1) {
        if (bf(node.left) < 0) node.left = leftRotate(node.left); // LR:先左旋左孩子
        return rightRotate(node);                                 // LL / LR 收尾:右旋
    }
    // 右偏
    if (bf < -1) {
        if (bf(node.right) > 0) node.right = rightRotate(node.right); // RL:先右旋右孩子
        return leftRotate(node);                                      // RR / RL 收尾:左旋
    }
    return node;   // 平衡,无需旋转
}

判断口诀还是「同向单旋、拐弯双旋」:失衡节点和高孩子同号就单旋,异号就先把孩子转成同号(掰直)再单旋。

四、走一个例子

依次插入 1, 2, 3

插 1、2 正常 → 插 3 后节点 1 的 BF=-2(RR 型)
  1                    2
   \      左旋 1      / \
    2     ——→        1   3
     \
      3

如果不平衡就退化成链,AVL 通过这一次左旋把它变成了完美平衡的树。

五、复杂度

  • 时间 O(log n):BST 插入下降 O(log n) + 回溯更新/旋转 O(log n),旋转本身 O(1)。
  • 旋转次数:最多 1 次(单旋或双旋)。
  • 空间 O(log n):递归栈(等于树高)。

六、常见误区与追问

考点正确口径
插入位置按 BST 规则插入到叶子
失衡检测回溯更新高度并检查平衡因子
修复方式LL/RR 单旋,LR/RL 双旋
insert like BST
update height upward
if balance > 1 or < -1:
  choose LL/RR/LR/RL rotation

AVL 插入的旋转点是从新节点向上遇到的第一个失衡祖先。

  • 误区:AVL 插入前就能确定旋转类型。 必须先按 BST 插入,再回溯看失衡节点和插入方向。
  • 误区:平衡因子只要不等于 0 就要旋转。 AVL 允许平衡因子为 -1、0、1,绝对值超过 1 才失衡。
  • 误区:LR 和 RL 只需要一次旋转。 LR/RL 是内侧插入导致的折线结构,需要先对子节点旋转,再对失衡节点旋转。
  • 追问:为什么插入通常一次旋转就够? 在第一个失衡祖先处旋转后,该子树高度恢复到插入前的高度,祖先不再继续失衡。
  • 追问:高度更新顺序是什么? 先更新较低层节点,再更新旋转后的新子树根,避免用旧高度判断。
  • 追问:复杂度是多少? AVL 高度 O(log n),插入查找和回溯都在树高内,时间 O(log n)。

七、加强记忆

AVL 插入三步:BST 插到叶子 → 自底向上更新祖先高度 → 遇到第一个 |BF|=2 的节点按 LL/RR/LR/RL 旋转。核心结论:插入最多一次旋转就能恢复整树平衡(因为旋转会把子树高度复原到插入前,上层不再失衡),这点优于删除。判断类型仍是「同向单旋、拐弯双旋」。整体 O(log n)。