红黑树
红黑树(Red-Black Tree)是一种自平衡二叉搜索树,通过给节点染色(红/黑)并施加五条性质,把树高限制在 $O(\log n)$。它比 AVL 树放松了平衡要求,插入删除的旋转更少,因此被广泛用于语言标准库(Java 的 TreeMap、C++ 的 std::map、Linux 内核的调度器等)。
五条性质
- 每个节点是红色或黑色。
- 根节点是黑色。
- 每个叶子(NIL 哨兵)是黑色。
- 红色节点的两个孩子都是黑色(不存在连续红节点)。
- 任一节点到其每个 NIL 子叶的路径都含相同数目的黑节点(黑高一致)。
性质 1-5 共同保证:最长路径不超过最短路径的两倍,树高 $O(\log n)$。
插入修复思路
新节点先染成红色(避免破坏性质 5),再根据"父亲与叔叔"的颜色调整:
- 叔叔红: 父、叔染黑,祖父染红,把问题上移。
- 叔叔黑(三角形): 先旋转把情形化为直线。
- 叔叔黑(直线): 父染黑、祖父染红,对祖父做一次相反方向的旋转。 最后把根染黑。
代码示例(Go语言实现)
go
package main
import "fmt"
type color int
const (
red color = iota
black
)
// RBNode 红黑树节点,NIL 哨兵的 Color 恒为 black
type RBNode struct {
Key int
Color color
Left *RBNode
Right *RBNode
Parent *RBNode // 根的 Parent 为 nil
}
// RedBlackTree 红黑树,nil 为黑色 NIL 哨兵
type RedBlackTree struct {
nil *RBNode
root *RBNode
}
// NewRedBlackTree 创建空树
func NewRedBlackTree() *RedBlackTree {
t := &RedBlackTree{}
t.nil = &RBNode{Color: black}
t.root = t.nil
return t
}
// leftRotate 左旋
func (t *RedBlackTree) leftRotate(x *RBNode) {
y := x.Right
x.Right = y.Left
if y.Left != t.nil {
y.Left.Parent = x
}
y.Parent = x.Parent
if x.Parent == nil {
t.root = y
} else if x == x.Parent.Left {
x.Parent.Left = y
} else {
x.Parent.Right = y
}
y.Left = x
x.Parent = y
}
// rightRotate 右旋
func (t *RedBlackTree) rightRotate(x *RBNode) {
y := x.Left
x.Left = y.Right
if y.Right != t.nil {
y.Right.Parent = x
}
y.Parent = x.Parent
if x.Parent == nil {
t.root = y
} else if x == x.Parent.Right {
x.Parent.Right = y
} else {
x.Parent.Left = y
}
y.Right = x
x.Parent = y
}
// Insert 插入 key(重复键忽略)
func (t *RedBlackTree) Insert(key int) {
var y *RBNode
x := t.root
for x != t.nil {
y = x
switch {
case key < x.Key:
x = x.Left
case key > x.Key:
x = x.Right
default:
return
}
}
z := &RBNode{Key: key, Color: red, Left: t.nil, Right: t.nil}
if y == nil { // 空树
t.root = z
} else {
z.Parent = y
if key < y.Key {
y.Left = z
} else {
y.Right = z
}
}
t.insertFixup(z)
}
// insertFixup 插入后恢复红黑性质
func (t *RedBlackTree) insertFixup(z *RBNode) {
for z.Parent != nil && z.Parent.Color == red {
if z.Parent == z.Parent.Parent.Left {
uncle := z.Parent.Parent.Right
if uncle.Color == red { // 情形 1:叔叔红
z.Parent.Color = black
uncle.Color = black
z.Parent.Parent.Color = red
z = z.Parent.Parent
} else {
if z == z.Parent.Right { // 情形 2:化为直线
z = z.Parent
t.leftRotate(z)
}
// 情形 3:旋转祖父
z.Parent.Color = black
z.Parent.Parent.Color = red
t.rightRotate(z.Parent.Parent)
}
} else { // 镜像情形
uncle := z.Parent.Parent.Left
if uncle.Color == red {
z.Parent.Color = black
uncle.Color = black
z.Parent.Parent.Color = red
z = z.Parent.Parent
} else {
if z == z.Parent.Left {
z = z.Parent
t.rightRotate(z)
}
z.Parent.Color = black
z.Parent.Parent.Color = red
t.leftRotate(z.Parent.Parent)
}
}
}
t.root.Color = black
}
// InOrder 中序遍历
func (t *RedBlackTree) InOrder() []int {
var out []int
var walk func(n *RBNode)
walk = func(n *RBNode) {
if n == t.nil {
return
}
walk(n.Left)
out = append(out, n.Key)
walk(n.Right)
}
walk(t.root)
return out
}
// validate 校验五条性质中可程序化验证的部分
func (t *RedBlackTree) validate() string {
if t.root.Color != black {
return "根不是黑色"
}
keys := t.InOrder()
for i := 1; i < len(keys); i++ {
if keys[i-1] >= keys[i] {
return "中序遍历不有序"
}
}
// 每条路径黑节点数一致,且无连续红节点
var check func(n *RBNode, redParent bool) (int, bool)
check = func(n *RBNode, redParent bool) (int, bool) {
if n == t.nil {
return 1, true // NIL 哨兵是黑色,计 1
}
if n.Color == red && redParent {
return 0, false // 连续红
}
lb, lok := check(n.Left, n.Color == red)
rb, rok := check(n.Right, n.Color == red)
if !lok || !rok || lb != rb {
return 0, false
}
if n.Color == black {
return lb + 1, true // 黑节点计入黑高
}
return lb, true // 红节点不计入
}
if _, ok := check(t.root, false); !ok {
return "黑高不一致或存在连续红节点"
}
return "合法"
}
func main() {
t := NewRedBlackTree()
for _, k := range []int{10, 20, 30, 15, 25, 40, 50, 60, 55} {
t.Insert(k)
}
fmt.Println("中序遍历:", t.InOrder())
fmt.Println("校验:", t.validate())
}运行输出:
text
中序遍历: [10 15 20 25 30 40 50 55 60]
校验: 合法复杂度分析
- 查找/插入/删除: $O(\log n)$。
- 空间: $O(n)$,额外颜色与父指针字段。
- 与 AVL 相比:查找略慢(允许更松的平衡),插入删除更快(旋转更少)。
总结
红黑树用染色规则代替严格的身高差,把"不会退化成链表"这件事变成了五条可验证的性质。插入修复的三种情形(叔叔红、三角形、直线)看似琐碎,实则每一步都在同时维护有序性与黑高平衡。