算法导论第十三章 红黑树:平衡的艺术

第十三章 红黑树:平衡的艺术

“平衡不是静止,而是动态的和谐。” —— 达芬奇

在二叉搜索树的世界中,红黑树如同一位优雅的舞者,在动态操作中保持着完美的平衡。本章将揭开这种高效数据结构的神秘面纱,探索它如何在插入和删除操作中保持优雅姿态。

13.1 红黑树的诞生:解决BST的致命缺陷

13.1.1 BST的退化问题

在第十二章中,我们看到二叉搜索树在极端情况下会退化为链表,操作复杂度从O(log n)恶化为O(n)。1972年,鲁道夫·拜尔(Rudolf Bayer)发明了红黑树的前身——对称二叉B树,1978年由利奥尼达斯·吉巴斯(Leonidas Guibas)和罗伯特·塞奇威克(Robert Sedgewick)完善为现代红黑树。

退化问题
二叉搜索树
平衡需求
AVL树
红黑树
伸展树
C++ STL
Java TreeMap
Linux内核

13.1.2 红黑树的五大公理

红黑树通过五个简单规则维持平衡:

  1. 颜色规则:每个节点非红即黑
  2. 根规则:根节点总是黑色
  3. 叶规则:所有叶子节点(NIL)是黑色
  4. 红节点规则:红色节点的子节点必须是黑色
  5. 黑高规则:从任一节点到其叶子的所有路径包含相同数目的黑色节点(黑高
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 左旋操作

左旋是红黑树保持平衡的核心操作之一:

节点x节点y子树α子树β子树γ旋转前结构父节点左子节点右子节点左子节点左旋操作保留成为x的父节点成为y的左子节点保留不变节点x节点y子树α子树β子树γ

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 插入算法步骤

  1. 按BST规则插入新节点Z,着为红色
  2. 调整颜色和旋转以维持红黑树性质
黑色
红色
红色
黑色
右孩子
左孩子
插入新节点Z为红色
父节点P颜色?
完成
叔节点U颜色?
P和U变黑
祖父G变红
Z=G
Z是P的?
Z=P
左旋Z
P变黑
G变红
右旋G

13.3.2 插入修复的三种情况

情况1:叔节点为红色

G黑
P红
U红
Z红
子树
子树
子树

修复:将P和U变黑,G变红,然后以G作为新Z继续调整

情况2:叔节点为黑,Z是右孩子

G黑
P红
U黑
子树
Z红

修复:左旋P,转化为情况3

情况3:叔节点为黑,Z是左孩子

G黑
P红
U黑
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 删除算法步骤

  1. 执行标准BST删除
  2. 如果删除节点是黑色,进行修复
红色
黑色
红色
黑色
红色
黑色
红色
黑色
删除节点Y
实际删除节点X颜色?
结束
兄弟节点W颜色?
W变黑
父P变红
旋转P
更新W
远侄子颜色?
远侄子变黑
W继承P颜色
P变黑
旋转P
近侄子颜色?
近侄子变黑
W变红
旋转W
更新W
W变红
X=P

13.4.2 删除修复的四种情况

情况1:兄弟W为红色

P黑
X黑
W红
A黑
B黑

修复:W变黑,P变红,左旋P,更新W

情况2:兄弟W为黑,W的两个子节点为黑

P任意
X黑
W黑
A黑
B黑

修复:W变红,X上移到P

情况3:兄弟W为黑,W的右子为黑(左子为红)

P任意
X黑
W黑
A红
B黑

修复:A变黑,W变红,右旋W,更新W

情况4:兄弟W为黑,W的右子为红

P任意
X黑
W黑
A任意
B红

修复: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 总结与下一章预告

红黑树通过五大规则和精妙的旋转操作,在动态变化中保持了树的平衡。它的设计体现了计算机科学中平衡效率与复杂性的深刻智慧:

  1. 通过颜色标记减少旋转次数
  2. 黑高相等保证最长路径不超过最短路径的两倍
  3. 插入和删除的修复操作局限在局部路径
  4. 最坏情况下仍保持O(log n)的操作效率
红黑树
五大规则
旋转操作
插入修复
删除修复
保持黑高
调整结构
三种情况
四种情况
平衡保证

下一章预告:B树与B+树

当数据量超过内存容量,红黑树不再适用。第十四章将探索B树与B+树——这些数据结构专为磁盘存储设计,能够高效管理海量数据:

  • B树的基本结构与性质
  • B树的插入、删除和分裂操作
  • B+树与B树的区别及优势
  • 在数据库系统中的实际应用
  • 现代文件系统如何利用B+树

通过精心设计的节点大小和分裂策略,B树家族在磁盘I/O和内存效率之间找到了完美平衡点。

红黑树作为平衡二叉搜索树的杰出代表,在理论和实践中都占有重要地位。掌握红黑树不仅有助于理解高级数据结构,更能培养解决复杂问题的系统化思维。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

W说编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值