旋转
概述
旋转(rotation) 是红黑树(以及 AVL 树等自平衡二叉搜索树)中的基本局部结构调整操作。旋转通过改变节点之间的父子关系来改变树的结构,分为**左旋(LEFT-ROTATE)和右旋(RIGHT-ROTATE)**两种。旋转操作在 时间内完成,是保持红黑树平衡的核心手段。
定义
左旋 LEFT-ROTATE(T, x)
对节点 执行左旋操作,假设 的右子节点 不为 NIL。左旋将 与其右子节点 的位置进行”局部交换”: 上升到 的位置, 变为 的左子节点, 的原左子节点变为 的右子节点。
右旋 RIGHT-ROTATE(T, y)
右旋是左旋的对称操作。对节点 执行右旋,假设 的左子节点 不为 NIL。右旋将 与其左子节点 的位置进行”局部交换”: 上升到 的位置, 变为 的右子节点, 的原右子节点变为 的左子节点。
左旋操作详解
操作步骤
设 为当前节点,,,,。
旋转前: 左旋后:
x y
/ \ / \
α y → x γ
/ \ / \
β γ α β
LEFT-ROTATE(T, x) 的具体步骤:
- 设 ( 不能为 NIL)
- 将 的左子树 “嫁接”为 的右子树:
- 如果 不为 NIL,设置 的父指针:
- 将 的父节点”嫁接”为 的父节点:
- 如果 为 NIL,则 成为新的根节点:
- 否则如果 是其父节点的左子节点:
- 否则:
- 将 设为 的左子节点:,
伪代码
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 一次左旋(或对称) |
参见
- 红黑树 — 红黑树的完整定义
- RB-INSERT-FIXUP — 插入修复中的旋转应用
- RB-DELETE-FIXUP — 删除修复中的旋转应用
- 二叉搜索树 — BST 的基本性质