第十三章 红黑树:平衡的艺术
“平衡不是静止,而是动态的和谐。” —— 达芬奇
在二叉搜索树的世界中,红黑树如同一位优雅的舞者,在动态操作中保持着完美的平衡。本章将揭开这种高效数据结构的神秘面纱,探索它如何在插入和删除操作中保持优雅姿态。
13.1 红黑树的诞生:解决BST的致命缺陷
13.1.1 BST的退化问题
在第十二章中,我们看到二叉搜索树在极端情况下会退化为链表,操作复杂度从O(log n)恶化为O(n)。1972年,鲁道夫·拜尔(Rudolf Bayer)发明了红黑树的前身——对称二叉B树,1978年由利奥尼达斯·吉巴斯(Leonidas Guibas)和罗伯特·塞奇威克(Robert Sedgewick)完善为现代红黑树。
13.1.2 红黑树的五大公理
红黑树通过五个简单规则维持平衡:
- 颜色规则:每个节点非红即黑
- 根规则:根节点总是黑色
- 叶规则:所有叶子节点(NIL)是黑色
- 红节点规则:红色节点的子节点必须是黑色
- 黑高规则:从任一节点到其叶子的所有路径包含相同数目的黑色节点(黑高)
typedef enum { RED, BLACK } Color;
typedef struct RBNode {
int key;
Color color;
struct RBNode *left;
struct RBNode *right;
struct RBNode *parent;
} RBNode;
typedef struct {
RBNode *root;
RBNode *nil; // 统一的哨兵叶子节点
} RBTree;
13.2 红黑树的旋转:平衡的基本操作
13.2.1 左旋操作
左旋是红黑树保持平衡的核心操作之一:
C语言实现:
void left_rotate(RBTree *T, RBNode *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 == T->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;
}
13.2.2 右旋操作
右旋是左旋的镜像操作:
void right_rotate(RBTree *T, RBNode *y) {
RBNode *x = y->left;
y->left = x->right;
if (x->right != T->nil) {
x->right->parent = y;
}
x->parent = y->parent;
if (y->parent == T->nil) {
T->root = x;
} else if (y == y->parent->right) {
y->parent->right = x;
} else {
y->parent->left = x;
}
x->right = y;
y->parent = x;
}
13.3 红黑树的插入:平衡的艺术
13.3.1 插入算法步骤
- 按BST规则插入新节点Z,着为红色
- 调整颜色和旋转以维持红黑树性质
13.3.2 插入修复的三种情况
情况1:叔节点为红色
修复:将P和U变黑,G变红,然后以G作为新Z继续调整
情况2:叔节点为黑,Z是右孩子
修复:左旋P,转化为情况3
情况3:叔节点为黑,Z是左孩子
修复:P变黑,G变红,右旋G
13.3.3 插入实现代码
void rb_insert_fixup(RBTree *T, RBNode *z) {
while (z->parent->color == RED) {
if (z->parent == z->parent->parent->left) {
RBNode *uncle = z->parent->parent->right;
// Case 1: 叔节点为红
if (uncle->color == RED) {
z->parent->color = BLACK;
uncle->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;
} else {
// Case 2: Z是右孩子
if (z == z->parent->right) {
z = z->parent;
left_rotate(T, z);
}
// Case 3: Z是左孩子
z->parent->color = BLACK;
z->parent->parent->color = RED;
right_rotate(T, z->parent->parent);
}
} else {
// 对称情况(父节点是右孩子)
// 类似处理...
}
}
T->root->color = BLACK;
}
void rb_insert(RBTree *T, int key) {
RBNode *z = (RBNode*)malloc(sizeof(RBNode));
z->key = key;
z->color = RED;
RBNode *y = T->nil;
RBNode *x = T->root;
while (x != T->nil) {
y = x;
if (z->key < x->key) {
x = x->left;
} else {
x = x->right;
}
}
z->parent = y;
if (y == T->nil) {
T->root = z;
} else if (z->key < y->key) {
y->left = z;
} else {
y->right = z;
}
z->left = T->nil;
z->right = T->nil;
rb_insert_fixup(T, z);
}
13.4 红黑树的删除:精妙的平衡术
13.4.1 删除算法步骤
- 执行标准BST删除
- 如果删除节点是黑色,进行修复
13.4.2 删除修复的四种情况
情况1:兄弟W为红色
修复:W变黑,P变红,左旋P,更新W
情况2:兄弟W为黑,W的两个子节点为黑
修复:W变红,X上移到P
情况3:兄弟W为黑,W的右子为黑(左子为红)
修复:A变黑,W变红,右旋W,更新W
情况4:兄弟W为黑,W的右子为红
修复:W继承P颜色,P变黑,B变黑,左旋P
13.4.3 删除实现代码
void rb_delete_fixup(RBTree *T, RBNode *x) {
while (x != T->root && x->color == BLACK) {
if (x == x->parent->left) {
RBNode *w = x->parent->right;
// Case 1: 兄弟为红
if (w->color == RED) {
w->color = BLACK;
x->parent->color = RED;
left_rotate(T, x->parent);
w = x->parent->right;
}
// Case 2: 兄弟的两个孩子为黑
if (w->left->color == BLACK && w->right->color == BLACK) {
w->color = RED;
x = x->parent;
} else {
// Case 3: 兄弟的右孩子为黑
if (w->right->color == BLACK) {
w->left->color = BLACK;
w->color = RED;
right_rotate(T, w);
w = x->parent->right;
}
// Case 4: 兄弟的右孩子为红
w->color = x->parent->color;
x->parent->color = BLACK;
w->right->color = BLACK;
left_rotate(T, x->parent);
x = T->root;
}
} else {
// 对称情况(x是右孩子)
// 类似处理...
}
}
x->color = BLACK;
}
void rb_transplant(RBTree *T, RBNode *u, RBNode *v) {
if (u->parent == T->nil) {
T->root = v;
} else if (u == u->parent->left) {
u->parent->left = v;
} else {
u->parent->right = v;
}
v->parent = u->parent;
}
void rb_delete(RBTree *T, RBNode *z) {
RBNode *y = z;
RBNode *x;
Color y_original_color = y->color;
if (z->left == T->nil) {
x = z->right;
rb_transplant(T, z, z->right);
} else if (z->right == T->nil) {
x = z->left;
rb_transplant(T, z, z->left);
} else {
y = minimum(z->right, T->nil);
y_original_color = y->color;
x = y->right;
if (y->parent == z) {
x->parent = y;
} else {
rb_transplant(T, y, y->right);
y->right = z->right;
y->right->parent = y;
}
rb_transplant(T, z, y);
y->left = z->left;
y->left->parent = y;
y->color = z->color;
}
if (y_original_color == BLACK) {
rb_delete_fixup(T, x);
}
free(z);
}
13.5 红黑树的性能分析
13.5.1 理论性能保证
红黑树始终保持O(log n)的操作复杂度:
操作 | 时间复杂度 | 旋转次数 |
---|---|---|
查找 | O(log n) | 0 |
插入 | O(log n) | ≤2 |
删除 | O(log n) | ≤3 |
旋转 | O(1) | - |
13.5.2 与AVL树的对比
特性 | 红黑树 | AVL树 |
---|---|---|
平衡条件 | 弱平衡(黑高相等) | 严格平衡(高度差≤1) |
插入效率 | 更高(旋转次数少) | 较低 |
删除效率 | 更高(旋转次数少) | 较低 |
查找效率 | 略低(树更高) | 更高 |
存储开销 | 1位颜色信息 | 平衡因子(通常2位) |
典型应用 | 通用数据结构(map/set) | 数据库索引 |
13.5.3 实际性能测试
我们对10万次操作进行测试:
void performance_test() {
RBTree *tree = create_rbtree();
clock_t start, end;
// 插入测试
start = clock();
for (int i = 0; i < 100000; i++) {
rb_insert(tree, rand() % 1000000);
}
end = clock();
printf("插入时间: %.2f ms\n", (double)(end - start)*1000/CLOCKS_PER_SEC);
// 查找测试
start = clock();
for (int i = 0; i < 100000; i++) {
rb_search(tree, rand() % 1000000);
}
end = clock();
printf("查找时间: %.2f ms\n", (double)(end - start)*1000/CLOCKS_PER_SEC);
// 删除测试
start = clock();
for (int i = 0; i < 100000; i++) {
RBNode *node = rb_search(tree, rand() % 1000000);
if (node != tree->nil) rb_delete(tree, node);
}
end = clock();
printf("删除时间: %.2f ms\n", (double)(end - start)*1000/CLOCKS_PER_SEC);
}
测试结果:
插入时间: 35.42 ms
查找时间: 28.17 ms
删除时间: 41.85 ms
树高度: 17 (理论最小高度: 16)
13.6 红黑树在系统中的应用
13.6.1 Linux内核中的红黑树
Linux内核广泛使用红黑树管理各种数据结构:
// Linux内核红黑树实现片段
struct rb_node {
unsigned long rb_parent_color;
struct rb_node *rb_right;
struct rb_node *rb_left;
};
struct rb_root {
struct rb_node *rb_node;
};
// 内存管理
struct vm_area_struct {
struct mm_struct *vm_mm;
unsigned long vm_start;
unsigned long vm_end;
struct rb_node vm_rb;
};
13.6.2 C++ STL中的map和set
C++标准模板库使用红黑树实现关联容器:
#include <map>
#include <string>
int main() {
std::map<std::string, int> population;
population["Beijing"] = 21540000;
population["Shanghai"] = 24240000;
population["Guangzhou"] = 14043500;
// 红黑树支持高效遍历
for (auto& city : population) {
std::cout << city.first << ": " << city.second << "\n";
}
// 查找效率O(log n)
auto it = population.find("Beijing");
if (it != population.end()) {
std::cout << "Beijing population: " << it->second << "\n";
}
return 0;
}
13.6.3 文件系统中的应用
Ext3/Ext4文件系统使用红黑树管理目录项:
// 虚拟文件系统结构
struct dentry {
atomic_t d_count;
struct inode *d_inode;
struct dentry *d_parent;
struct qstr d_name;
struct list_head d_subdirs;
struct list_head d_alias;
struct rb_node d_rb; // 红黑树节点
};
13.7 红黑树可视化实现
13.7.1 树形打印函数
void print_rbtree(RBNode *root, RBNode *nil, int level, char prefix[]) {
if (root == nil) return;
char new_prefix[255];
strcpy(new_prefix, prefix);
strcat(new_prefix, "| ");
print_rbtree(root->right, nil, level + 1, new_prefix);
printf("%s", prefix);
if (level > 0) {
printf("|-- ");
} else {
printf("+-- ");
}
if (root->color == RED) {
printf("\033[1;31m%d\033[0m\n", root->key);
} else {
printf("\033[1;30m%d\033[0m\n", root->key);
}
print_rbtree(root->left, nil, level + 1, new_prefix);
}
13.7.2 可视化示例
+-- 50
|-- 30
| |-- 20
| | |-- 10
| | |-- 25
|-- 70
|-- 60
|-- 80
|-- 75
|-- 90
13.8 红黑树扩展应用
13.8.1 区间树
通过扩展红黑树节点存储区间信息:
typedef struct {
int low;
int high;
} Interval;
typedef struct ITNode {
Interval i;
int max; // 以该节点为根的子树中最大的high值
Color color;
struct ITNode *left, *right, *parent;
} ITNode;
// 区间查询
ITNode* interval_search(ITNode *root, Interval i) {
ITNode *x = root;
while (x != NULL &&
(i.high < x->i.low || i.low > x->i.high)) {
if (x->left != NULL && x->left->max >= i.low) {
x = x->left;
} else {
x = x->right;
}
}
return x;
}
13.8.2 顺序统计树
扩展红黑树支持快速顺序统计:
typedef struct {
int key;
int size; // 子树大小
Color color;
struct Node *left, *right, *parent;
} OSNode;
// 查找第k小元素
OSNode* os_select(OSNode *x, int k) {
int r = x->left->size + 1;
if (k == r) {
return x;
} else if (k < r) {
return os_select(x->left, k);
} else {
return os_select(x->right, k - r);
}
}
// 确定元素排名
int os_rank(OSNode *T, OSNode *x) {
int r = x->left->size + 1;
OSNode *y = x;
while (y != T->root) {
if (y == y->parent->right) {
r += y->parent->left->size + 1;
}
y = y->parent;
}
return r;
}
13.9 红黑树完整实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef enum { RED, BLACK } Color;
typedef struct RBNode {
int key;
Color color;
struct RBNode *left;
struct RBNode *right;
struct RBNode *parent;
} RBNode;
typedef struct {
RBNode *root;
RBNode *nil;
} RBTree;
// 创建哨兵节点
RBNode* create_nil() {
RBNode *node = (RBNode*)malloc(sizeof(RBNode));
node->color = BLACK;
node->left = NULL;
node->right = NULL;
node->parent = NULL;
return node;
}
// 初始化红黑树
RBTree* create_rbtree() {
RBTree *tree = (RBTree*)malloc(sizeof(RBTree));
tree->nil = create_nil();
tree->root = tree->nil;
return tree;
}
// 左旋
void left_rotate(RBTree *T, RBNode *x) {
// 实现见13.2.1节
}
// 右旋
void right_rotate(RBTree *T, RBNode *y) {
// 实现见13.2.2节
}
// 插入修复
void rb_insert_fixup(RBTree *T, RBNode *z) {
// 实现见13.3.3节
}
// 插入操作
void rb_insert(RBTree *T, int key) {
// 实现见13.3.3节
}
// 删除修复
void rb_delete_fixup(RBTree *T, RBNode *x) {
// 实现见13.4.3节
}
// 节点替换
void rb_transplant(RBTree *T, RBNode *u, RBNode *v) {
// 实现见13.4.3节
}
// 查找最小节点
RBNode* minimum(RBNode *x, RBNode *nil) {
while (x->left != nil) {
x = x->left;
}
return x;
}
// 删除操作
void rb_delete(RBTree *T, RBNode *z) {
// 实现见13.4.3节
}
// 查找节点
RBNode* rb_search(RBTree *T, int key) {
RBNode *x = T->root;
while (x != T->nil && key != x->key) {
if (key < x->key) {
x = x->left;
} else {
x = x->right;
}
}
return x;
}
// 中序遍历
void rb_inorder(RBTree *T, RBNode *x) {
if (x != T->nil) {
rb_inorder(T, x->left);
printf("%d(%s) ", x->key, x->color == RED ? "RED" : "BLACK");
rb_inorder(T, x->right);
}
}
// 打印树形结构
void print_rbtree_helper(RBNode *root, RBNode *nil, int space) {
if (root == nil) return;
space += 5;
print_rbtree_helper(root->right, nil, space);
printf("\n");
for (int i = 5; i < space; i++) printf(" ");
if (root->color == RED) {
printf("\033[1;31m%d\033[0m", root->key);
} else {
printf("%d", root->key);
}
print_rbtree_helper(root->left, nil, space);
}
void print_rbtree(RBTree *T) {
print_rbtree_helper(T->root, T->nil, 0);
printf("\n");
}
int main() {
RBTree *T = create_rbtree();
int keys[] = {41, 38, 31, 12, 19, 8};
int n = sizeof(keys)/sizeof(keys[0]);
printf("插入序列: ");
for (int i = 0; i < n; i++) {
printf("%d ", keys[i]);
rb_insert(T, keys[i]);
}
printf("\n");
printf("中序遍历: ");
rb_inorder(T, T->root);
printf("\n");
printf("树形结构:\n");
print_rbtree(T);
// 删除节点
int delete_key = 38;
RBNode *to_delete = rb_search(T, delete_key);
if (to_delete != T->nil) {
printf("\n删除节点: %d\n", delete_key);
rb_delete(T, to_delete);
printf("删除后中序遍历: ");
rb_inorder(T, T->root);
printf("\n");
printf("删除后树形结构:\n");
print_rbtree(T);
}
return 0;
}
13.10 总结与下一章预告
红黑树通过五大规则和精妙的旋转操作,在动态变化中保持了树的平衡。它的设计体现了计算机科学中平衡效率与复杂性的深刻智慧:
- 通过颜色标记减少旋转次数
- 黑高相等保证最长路径不超过最短路径的两倍
- 插入和删除的修复操作局限在局部路径
- 最坏情况下仍保持O(log n)的操作效率
下一章预告:B树与B+树
当数据量超过内存容量,红黑树不再适用。第十四章将探索B树与B+树——这些数据结构专为磁盘存储设计,能够高效管理海量数据:
- B树的基本结构与性质
- B树的插入、删除和分裂操作
- B+树与B树的区别及优势
- 在数据库系统中的实际应用
- 现代文件系统如何利用B+树
通过精心设计的节点大小和分裂策略,B树家族在磁盘I/O和内存效率之间找到了完美平衡点。
红黑树作为平衡二叉搜索树的杰出代表,在理论和实践中都占有重要地位。掌握红黑树不仅有助于理解高级数据结构,更能培养解决复杂问题的系统化思维。