旋转

概述

旋转(rotation) 是红黑树(以及 AVL 树等自平衡二叉搜索树)中的基本局部结构调整操作。旋转通过改变节点之间的父子关系来改变树的结构,分为**左旋(LEFT-ROTATE)右旋(RIGHT-ROTATE)**两种。旋转操作在 时间内完成,是保持红黑树平衡的核心手段。

定义

左旋 LEFT-ROTATE(T, x)

对节点 执行左旋操作,假设 右子节点 不为 NIL。左旋将 与其右子节点 的位置进行”局部交换”: 上升到 的位置, 变为 的左子节点, 的原左子节点变为 的右子节点。

右旋 RIGHT-ROTATE(T, y)

右旋是左旋的对称操作。对节点 执行右旋,假设 左子节点 不为 NIL。右旋将 与其左子节点 的位置进行”局部交换”: 上升到 的位置, 变为 的右子节点, 的原右子节点变为 的左子节点。

左旋操作详解

操作步骤

为当前节点,

旋转前:          左旋后:
    x                y
   / \              / \
  α   y     →      x   γ
     / \          / \
    β   γ        α   β

LEFT-ROTATE(T, x) 的具体步骤:

  1. 不能为 NIL)
  2. 的左子树 “嫁接”为 的右子树:
  3. 如果 不为 NIL,设置 的父指针:
  4. 的父节点”嫁接”为 的父节点:
  5. 如果 为 NIL,则 成为新的根节点:
  6. 否则如果 是其父节点的左子节点:
  7. 否则:
  8. 设为 的左子节点:

伪代码

LEFT-ROTATE(T, x)
  y = x.right
  x.right = y.left          // 将 y 的左子树转为 x 的右子树
  if y.left ≠ T.nil
      y.left.p = x
  y.p = x.p                 // 将 y "挂到" x 的父节点上
  if x.p == T.nil
      T.root = y
  elseif x == x.p.left
      x.p.left = y
  else
      x.p.right = y
  y.left = x                // 将 x 放到 y 的左边
  x.p = y

右旋操作详解

右旋是左旋的完全对称操作,只需将”左”与”右”互换。

旋转前:          右旋后:
      y            x
     / \          / \
    x   γ   →    α   y
   / \              / \
  α   β            β   γ

核心性质

时间复杂度

时间复杂度

旋转操作只修改常数个指针(最多修改 6 个指针),因此时间复杂度为

保持 BST 性质

BST 性质保持

旋转操作保持二叉搜索树的性质。旋转前后,树的中序遍历结果完全相同。

证明(以左旋为例):

左旋前,中序遍历顺序为:

左旋后, 的左子树仍为 ,右子树变为 的左子树变为 (包含 ),右子树仍为

中序遍历:先遍历 的左子树(即以 为根的子树),得到 ;然后访问 ;最后遍历 的右子树

结果仍为:

不保持红黑性质

重要

旋转操作不保持红黑性质。旋转可能破坏红黑树的某些性质(如性质 4 或性质 5),因此旋转通常作为修复操作的子步骤,与变色操作配合使用来恢复红黑性质。

左旋与右旋的对称性

  • 左旋和右旋互为逆操作:对 执行左旋得到 后,再对 执行右旋,可以恢复原来的树结构。
  • 左旋要求 有右子节点;右旋要求 有左子节点。

章节扩展

旋转在红黑树中的两个核心应用场景:

场景旋转次数说明
插入修复 RB-INSERT-FIXUP最多 2 次Case 2 一次左旋 + Case 3 一次右旋(或对称)
删除修复 RB-DELETE-FIXUP最多 3 次Case 1 一次左旋 + Case 3 一次右旋 + Case 4 一次左旋(或对称)

参见