目录
一、原理
二、操作示例
三、应用场景
四、C语言实现红黑树
五、代码说明
六、红黑树和AVL树对比
一、原理
熟悉红黑树之前,我们需要了解二叉树与二叉查找树概念,参见前述相关文章:二叉查找树BST详解及其C语言实现-CSDN博客
红黑树是一种自平衡的二叉查找树,满足以下5个核心性质:
-
每个节点是红色或黑色
-
根节点是黑色
-
所有叶子节点(NIL节点)是黑色
-
红色节点的子节点必须是黑色
-
从任一节点到其叶子节点的所有路径包含相同数量的黑色节点
红黑树示例图
二、操作示例
以插入序列[3, 21, 32, 15]为例:
插入3(根节点):3(B)插入21:3(B)\21(R)插入32(需旋转):3(B) 21(B)\ 右旋 / \21(R) → 3(R) 32(R)\32(R)插入15:21(B)/ \3(R) 32(B)\15(R)
(符合红黑树性质,无需调整)
三、应用场景
-
C++ STL map/multimap、set/multiset
-
Java TreeMap/TreeSet
-
Linux内核进程调度
-
文件系统目录结构
-
实时计算系统
-
内存管理中的虚拟地址空间管理
四、C语言实现红黑树
#include <stdio.h>
#include <stdlib.h>typedef enum { RED, BLACK } Color;typedef struct Node {int key;Color color;struct Node *left, *right, *parent;
} Node;Node *NIL_LEAF; // 哨兵节点Node* create_node(int key) {Node* node = (Node*)malloc(sizeof(Node));node->key = key;node->color = RED;node->left = node->right = NIL_LEAF;node->parent = NIL_LEAF;return node;
}void left_rotate(Node **root, Node *x) {Node *y = x->right;x->right = y->left;if (y->left != NIL_LEAF)y->left->parent = x;y->parent = x->parent;if (x->parent == NIL_LEAF)*root = y;else if (x == x->parent->left)x->parent->left = y;elsex->parent->right = y;y->left = x;x->parent = y;
}void right_rotate(Node **root, Node *y) {Node *x = y->left;y->left = x->right;if (x->right != NIL_LEAF)x->right->parent = y;x->parent = y->parent;if (y->parent == NIL_LEAF)*root = x;else if (y == y->parent->left)y->parent->left = x;elsey->parent->right = x;x->right = y;y->parent = x;
}void fix_insert(Node **root, Node *z) {while (z->parent->color == RED) {if (z->parent == z->parent->parent->left) {Node *y = z->parent->parent->right;if (y->color == RED) {z->parent->color = BLACK;y->color = BLACK;z->parent->parent->color = RED;z = z->parent->parent;} else {if (z == z->parent->right) {z = z->parent;left_rotate(root, z);}z->parent->color = BLACK;z->parent->parent->color = RED;right_rotate(root, z->parent->parent);}} else {// 对称处理右子树情况}if (z == *root) break;}(*root)->color = BLACK;
}void insert(Node **root, int key) {Node *z = create_node(key);Node *y = NIL_LEAF;Node *x = *root;while (x != NIL_LEAF) {y = x;if (z->key < x->key)x = x->left;elsex = x->right;}z->parent = y;if (y == NIL_LEAF)*root = z;else if (z->key < y->key)y->left = z;elsey->right = z;fix_insert(root, z);
}// 中序遍历验证
void inorder(Node *node) {if (node != NIL_LEAF) {inorder(node->left);printf("%d(%s) ", node->key, node->color == RED ? "R" : "B");inorder(node->right);}
}int main() {// 初始化NIL节点NIL_LEAF = (Node*)malloc(sizeof(Node));NIL_LEAF->color = BLACK;NIL_LEAF->left = NIL_LEAF->right = NIL_LEAF->parent = NULL;Node *root = NIL_LEAF;insert(&root, 3);insert(&root, 21);insert(&root, 32);insert(&root, 15);printf("Inorder traversal: ");inorder(root);printf("\nRoot: %d(%s)\n", root->key, root->color == RED ? "R" : "B");return 0;
}
五、代码说明
-
使用NIL节点作为统一的叶子节点
-
实现左旋/右旋操作维护平衡
-
插入修复处理三种情况:
-
叔节点为红色
-
叔节点为黑且当前节点为右子
-
叔节点为黑且当前节点为左子
-
-
通过颜色翻转和旋转操作保证红黑树性质
输出结果示例:
Inorder traversal: 3(R) 15(R) 21(B) 32(R)
Root: 21(B)
该实现可用于构建高性能字典结构,时间复杂度保持在O(log n)级别,适用于需要频繁插入删除的场景。实际工程中可扩展支持泛型、删除操作和内存回收等功能。
六、红黑树和AVL树对比
以下是详细对比数据结构中AVL树和红黑树的优缺点及其适合使用的应用场景:
AVL树 | 红黑树 | |
---|---|---|
平衡性 | 严格平衡,左右子树高度差不超过1 | 弱平衡,黑色节点路径长度一致,红色节点不连续 |
时间复杂度 | 查找、插入、删除均为O(log n) | 查找、插入、删除在最坏情况下为O(log n) |
优点 | 1. 查找效率高,因为树的高度始终保持较低 2. 适用于需要频繁查找但插入和删除次数较少的场景 | 1. 插入和删除操作更灵活,需要较少的旋转 2. 内存占用相对较低,因为节点不需要额外的平衡因子 |
缺点 | 1. 插入和删除操作可能需要频繁的旋转来维持平衡,增加了代码复杂度和运行开销 2. 节点需要额外维护平衡因子,增加了空间复杂度 | 1. 平衡性较弱,可能导致某些操作效率不如AVL树 2. 实现相对复杂,需要处理颜色和旋转规则 |
应用场景 | 1. 搜索操作频繁、插入和删除操作相对较少的场景 2. 对树的平衡性要求较高的场景,如某些数据库索引 | 1. 插入和删除操作较频繁、搜索操作相对较少的场景 2. 内存受限或需要高效迭代访问的场景,如内存管理、文件系统等 |
总结:
- AVL树在保持严格平衡方面表现出色,适用于需要频繁查找且插入和删除次数较少的场景。然而,由于需要频繁的旋转操作和额外的平衡因子,其代码复杂度和空间复杂度相对较高。
- 红黑树通过牺牲部分平衡性来换取更高的插入和删除效率,适用于插入和删除操作较频繁且搜索操作相对较少的场景。其内存占用相对较低,实现也相对简单,但需要处理颜色和旋转规则。
在选择使用哪种树结构时,需要根据具体的应用需求和性能要求来进行权衡。