【数据结构与算法】第29篇:红黑树原理与C语言模拟

一、红黑树的定义

1.1 五大性质

红黑树是一种自平衡二叉查找树,每个节点增加一个颜色属性(红或黑),必须满足:

性质 说明
性质1 每个节点是红色或黑色
性质2 根节点是黑色
性质3 所有叶子节点(NIL)是黑色
性质4 红色节点的两个子节点都是黑色(不能有连续红)
性质5 从任一节点到其每个叶子节点的所有路径都包含相同数目的黑色节点(黑高相同)

关键推论:红黑树的最长路径不超过最短路径的2倍,因此大致平衡。

1.2 节点结构

c

复制代码
#define RED 0
#define BLACK 1

typedef struct RBNode {
    int data;
    int color;              // RED 或 BLACK
    struct RBNode *left;
    struct RBNode *right;
    struct RBNode *parent;  // 需要父指针
} RBNode, *RBTree;

二、红黑树与AVL树的对比

对比项 AVL树 红黑树
平衡性 严格(高度差≤1) 大致(黑高相同)
树高 约 1.44 log n 约 2 log n
插入旋转 最多2次 最多3次
删除旋转 最多O(log n)次 最多3次
查找效率 更快 略慢
插入/删除效率 较慢 更快
实现复杂度 中等 较难
工程应用 较少 广泛

结论:查找多选AVL,插入删除多选红黑树。


三、插入操作的核心规则

3.1 插入规则

新插入的节点默认为红色(不破坏黑高)。

需要调整的情况(新节点N,父节点P,祖父G,叔父U):

情况 条件 处理
情况1 空树 插入根节点,改为黑色
情况2 P为黑色 无需调整
情况3 P为红色,U为红色 变色:P和U变黑,G变红,递归处理G
情况4 P为红色,U为黑色,N、P、G呈直线 旋转+变色
情况5 P为红色,U为黑色,N、P、G呈折线 双旋转+变色

3.2 变色规则图示

text

复制代码
情况3(P红,U红):
      G(黑)              G(红)
     /    \    变色→    /    \
   P(红)  U(红)       P(黑)  U(黑)
   /                 /
  N(红)             N(红)

text

复制代码
情况4(P红,U黑,LL型):
      G(黑)              P(黑)
     /    \    右旋→    /    \
   P(红)  U(黑)       N(红)  G(红)
   /                         \
  N(红)                       U(黑)

text

复制代码
情况4(RR型)对称处理(左旋)

情况5(LR型):
      G(黑)               G(黑)               N(黑)
     /    \    左旋→     /    \    右旋→    /    \
   P(红)  U(黑)        N(红)  U(黑)       P(红)  G(红)
     \                /                           \
     N(红)          P(红)                         U(黑)

情况5(RL型)对称处理(先右旋后左旋)

3.3 插入代码框架

c

复制代码
// 左旋(与AVL类似,需维护parent)
void leftRotate(RBTree *root, RBNode *x) {
    RBNode *y = x->right;
    x->right = y->left;
    if (y->left != NULL) y->left->parent = x;
    y->parent = x->parent;
    if (x->parent == NULL) *root = y;
    else if (x == x->parent->left) x->parent->left = y;
    else x->parent->right = y;
    y->left = x;
    x->parent = y;
}

// 右旋(对称)
void rightRotate(RBTree *root, RBNode *y) {
    // 对称实现...
}

// 插入后调整
void insertFixup(RBTree *root, RBNode *z) {
    while (z != *root && z->parent->color == RED) {
        if (z->parent == z->parent->parent->left) {
            RBNode *y = z->parent->parent->right; // 叔父
            if (y != NULL && y->color == RED) {
                // 情况3:变色
                z->parent->color = BLACK;
                y->color = BLACK;
                z->parent->parent->color = RED;
                z = z->parent->parent;
            } else {
                if (z == z->parent->right) {
                    // 情况5:LR型
                    z = z->parent;
                    leftRotate(root, z);
                }
                // 情况4:LL型
                z->parent->color = BLACK;
                z->parent->parent->color = RED;
                rightRotate(root, z->parent->parent);
            }
        } else {
            // 对称情况(P是右孩子)
            // ...
        }
    }
    (*root)->color = BLACK;
}

四、C语言模拟(简化版)

由于完整红黑树代码量很大(约300-500行),这里实现一个简化模拟,重点演示插入和调整逻辑。

c

复制代码
#include <stdio.h>
#include <stdlib.h>

#define RED 0
#define BLACK 1

typedef struct RBNode {
    int data;
    int color;
    struct RBNode *left;
    struct RBNode *right;
    struct RBNode *parent;
} RBNode, *RBTree;

// 创建节点
RBNode* createNode(int data) {
    RBNode *node = (RBNode*)malloc(sizeof(RBNode));
    node->data = data;
    node->color = RED;  // 新节点默认为红色
    node->left = NULL;
    node->right = NULL;
    node->parent = NULL;
    return node;
}

// 左旋
void leftRotate(RBTree *root, RBNode *x) {
    RBNode *y = x->right;
    x->right = y->left;
    if (y->left != NULL) y->left->parent = x;
    y->parent = x->parent;
    if (x->parent == NULL) *root = y;
    else if (x == x->parent->left) x->parent->left = y;
    else x->parent->right = y;
    y->left = x;
    x->parent = y;
}

// 右旋
void rightRotate(RBTree *root, RBNode *y) {
    RBNode *x = y->left;
    y->left = x->right;
    if (x->right != NULL) x->right->parent = y;
    x->parent = y->parent;
    if (y->parent == NULL) *root = x;
    else if (y == y->parent->left) y->parent->left = x;
    else y->parent->right = x;
    x->right = y;
    y->parent = x;
}

// 插入调整
void insertFixup(RBTree *root, RBNode *z) {
    while (z != *root && z->parent->color == RED) {
        if (z->parent == z->parent->parent->left) {
            RBNode *y = z->parent->parent->right;
            if (y != NULL && y->color == RED) {
                // 情况3:叔父红色,变色
                z->parent->color = BLACK;
                y->color = BLACK;
                z->parent->parent->color = RED;
                z = z->parent->parent;
            } else {
                if (z == z->parent->right) {
                    // 情况5:LR型
                    z = z->parent;
                    leftRotate(root, z);
                }
                // 情况4:LL型
                z->parent->color = BLACK;
                z->parent->parent->color = RED;
                rightRotate(root, z->parent->parent);
            }
        } else {
            // 对称情况(P是右孩子)
            RBNode *y = z->parent->parent->left;
            if (y != NULL && y->color == RED) {
                z->parent->color = BLACK;
                y->color = BLACK;
                z->parent->parent->color = RED;
                z = z->parent->parent;
            } else {
                if (z == z->parent->left) {
                    z = z->parent;
                    rightRotate(root, z);
                }
                z->parent->color = BLACK;
                z->parent->parent->color = RED;
                leftRotate(root, z->parent->parent);
            }
        }
    }
    (*root)->color = BLACK;
}

// 插入
void insert(RBTree *root, int data) {
    RBNode *z = createNode(data);
    RBNode *y = NULL;
    RBNode *x = *root;
    
    // 普通BST插入
    while (x != NULL) {
        y = x;
        if (z->data < x->data) x = x->left;
        else x = x->right;
    }
    z->parent = y;
    if (y == NULL) *root = z;
    else if (z->data < y->data) y->left = z;
    else y->right = z;
    
    // 调整红黑树性质
    insertFixup(root, z);
}

// 中序遍历
void inorder(RBNode *root) {
    if (root == NULL) return;
    inorder(root->left);
    printf("%d(%s) ", root->data, root->color == RED ? "红" : "黑");
    inorder(root->right);
}

// 打印树结构(简化)
void printTree(RBNode *root, int level) {
    if (root == NULL) return;
    printTree(root->right, level + 1);
    for (int i = 0; i < level; i++) printf("    ");
    printf("%d(%s)\n", root->data, root->color == RED ? "红" : "黑");
    printTree(root->left, level + 1);
}

int main() {
    RBTree root = NULL;
    
    printf("=== 红黑树插入演示 ===\n");
    int values[] = {10, 20, 30, 15, 25, 5, 1};
    
    for (int i = 0; i < 7; i++) {
        insert(&root, values[i]);
        printf("\n插入 %d 后:\n", values[i]);
        printf("中序遍历: ");
        inorder(root);
        printf("\n树结构:\n");
        printTree(root, 0);
    }
    
    return 0;
}

运行结果(部分):

text

复制代码
=== 红黑树插入演示 ===

插入 10 后:
中序遍历: 10(黑) 
树结构:
10(黑)

插入 20 后:
中序遍历: 10(黑) 20(红) 
树结构:
    20(红)
10(黑)

插入 30 后:
中序遍历: 10(红) 20(黑) 30(红) 
树结构:
        30(红)
    20(黑)
10(红)

插入 15 后:
中序遍历: 10(黑) 15(红) 20(黑) 30(黑) 
树结构:
        30(黑)
    20(黑)
        15(红)
10(黑)

...

五、红黑树的应用

应用 说明
C++ STL map/set 底层是红黑树
Java TreeMap/TreeSet 底层是红黑树
Linux内核 调度、内存管理、文件系统
epoll事件驱动 红黑树管理文件描述符
Nginx定时器 红黑树管理定时事件

六、小结

这一篇我们学习了红黑树的核心知识:

要点 说明
五大性质 根黑、叶黑、红不连、黑高相同
插入规则 新节点红,根据叔父颜色分情况处理
核心操作 变色、左旋、右旋
复杂度 查找/插入/删除 O(log n)
工程地位 最广泛应用的平衡树

红黑树 vs AVL树

  • AVL:严格平衡,查找快,插入删除慢

  • 红黑树:大致平衡,查找略慢,插入删除快

下一篇我们讲哈希表。


七、思考题

  1. 为什么红黑树新插入的节点是红色的?

  2. 红黑树的性质5(黑高相同)如何保证树的平衡?

  3. 如果连续插入相同的值,红黑树会如何处理?

  4. 查找操作比AVL树慢,但为什么工程中更常用红黑树?

欢迎在评论区讨论你的答案。

相关推荐
2501_9333295519 小时前
媒介宣发技术实践:Infoseek舆情系统的AI中台架构与应用解析
开发语言·人工智能·架构·数据库开发
DuHz20 小时前
论文精读:大语言模型 (Large Language Models, LLM) —— 一项调查
论文阅读·人工智能·深度学习·算法·机器学习·计算机视觉·语言模型
[J] 一坚20 小时前
嵌入式高手C
c语言·开发语言·stm32·单片机·mcu·51单片机·iot
odoo中国20 小时前
Odoo 19技术教程 : 如何在 Odoo 19 中创建 Many2one 组件
开发语言·odoo·odoo19·odoo技术·many2one
加农炮手Jinx20 小时前
LeetCode 72. Edit Distance 题解
算法·leetcode·力扣
借雨醉东风20 小时前
程序分享--常见算法/编程面试题:旋转矩阵
c++·线性代数·算法·面试·职场和发展·矩阵
逻辑驱动的ken20 小时前
Java高频面试考点场景题14
java·开发语言·深度学习·面试·职场和发展·求职招聘·春招
_深海凉_21 小时前
LeetCode热题100-打家劫舍
算法·leetcode·职场和发展
jghhh0121 小时前
使用 MATLAB 实现支持向量回归 (SVR) 预测未来数据
算法·matlab
云泽8081 天前
笔试算法 - 双指针篇(二):四大经典求和题型 + 有效三角形计数问题
c++·算法