【数据结构与算法】第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树慢,但为什么工程中更常用红黑树?

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

相关推荐
叹一曲当时只道是寻常2 小时前
Tauri v2 + Rust 实现 MCP Inspector 桌面应用:进程管理、Token 捕获与跨平台踩坑全记录
开发语言·后端·rust
生信研究猿2 小时前
94. 二叉树的中序遍历 (二叉树遍历整理)
数据结构·算法
挂科边缘2 小时前
image-restoration-sde复现,图像修复,使用均值回复随机微分方程进行图像修复,ICML 2023
算法·均值算法·ir-sde·扩散模块图像修复
2301_822703202 小时前
开源鸿蒙跨平台Flutter开发:血氧饱和度数据降噪:基于滑动窗口的滤波算法优化-利用动态列队 (Queue) 与时间窗口平滑光电容积脉搏波 (PPG)
算法·flutter·华为·开源·harmonyos
独特的螺狮粉2 小时前
开源鸿蒙跨平台Flutter开发:应对重症监护警报疲劳:BLoC 架构下的 FSM (有限状态机) 建模与全局消息干预机制
开发语言·flutter·华为·开源·harmonyos
Vin0sen2 小时前
算法-线段树与树状数组
算法
路飞雪吖~2 小时前
【测试】接口测试---1个框架,5个模块
开发语言·python·测试工具
sycmancia2 小时前
QT——计算器核心算法
开发语言·qt·算法
倦王2 小时前
力扣日刷45
算法·leetcode·职场和发展