红黑树是一种自平衡二叉查找树,它能够保证在最坏情况下基本的动态集合操作的时间复杂度为O(log n),其中n是树中的节点数。红黑树的每个节点都包含一个额外的颜色位,表示节点是红色还是黑色。红黑树的一些主要特性和操作如下:
红黑树的特性
1. 每个节点是红色或黑色。
2. 根节点是黑色。
3. 所有叶子节点是黑色。
4. 如果一个节点是红色的,则它的两个子节点都是黑色的。
5. 从任何一个节点到其每个叶子节点的所有简单路径上,均包含相同数目的黑色节点。
其中第四点不难看出了:
- 红色节点的父节点都是黑色
- 从根节点到叶子节点的所有路径上不能有 2 个连续的红色节点
这些特性确保了红黑树的高度近似平衡,从而保证了基本操作的时间复杂度为 O(log n)。
红黑树中有一些基本操作如:
旋转操作:
旋转是红黑树中非常重要的操作,用于维持树的平衡。旋转分为左旋和右旋。
插入操作:
插入新节点后,可能会违反红黑树的性质,需要进行修复。插入操作包括以下步骤:
1. 将新节点插入红黑树,并将其着色为红色。
2. 修复红黑树的性质。
删除操作:
删除节点后,同样可能会违反红黑树的性质,需要进行修复。删除操作包括以下步骤:
1. 找到并删除节点。
2. 修复红黑树的性质。
红黑树在计算机科学中有广泛的应用。由于其高效的查询、插入和删除操作,它在很多实际场景中被广泛使用。以下是一些红黑树常见的应用场景
1. 数据库系统
数据库系统需要高效地执行插入、删除和查找操作,以维护大量数据。红黑树由于其平衡特性,确保了这些操作在最坏情况下的时间复杂度为 O(logn)O(\log n)O(logn),因此被广泛用于数据库索引(如 B+ 树的基础)和内存中的数据结构(如数据库事务管理中的锁管理)。
2. 操作系统内核
红黑树在操作系统内核中有多个应用,例如:
- 进程调度:用于管理进程的调度队列,确保高效地插入、删除和查找进程。
- 内存管理:用于管理虚拟内存区域,跟踪空闲和已分配的内存块。
3. 高性能缓存
在缓存系统中,红黑树用于实现高效的缓存替换策略,例如 Least Recently Used (LRU) 缓存。红黑树帮助实现快速的插入、删除和查找缓存条目。
4. 网络路由
在网络路由协议(如 OSPF 和 BGP)中,红黑树用于维护路由表和前缀匹配表,以支持快速的路由查找和更新操作。
5. 集合和映射容器
红黑树在许多编程语言的标准库中实现集合(Set)和映射(Map)容器,例如:C++ STL:std::set
和 std::map
都是基于红黑树实现的。
6. 动态顺序统计
红黑树可以扩展用于动态顺序统计问题,例如找到序列中的第 k 大元素或求区间的和。这种扩展通常通过在节点中存储额外的信息(如子树大小或子树和)来实现。
下面是一个使用cpp实现红黑树的示例,实现了一个简单的红黑树,包含了一些基本功能,如插入删除,旋转,并提供了查找第 k 大元素的功能:
#include <iostream>
#include <limits>
enum Color { RED, BLACK };
// 定义节点结构体
struct Node {
int data; // 数据
Color color; // 颜色
Node *left, *right, *parent; // 左子节点,右子节点和父节点
int size; // 子树大小
// 构造函数
Node(int data) : data(data), color(RED), left(nullptr), right(nullptr), parent(nullptr), size(1) {}
};
class RedBlackTree {
private:
Node *root; // 根节点
// 左旋操作
void rotateLeft(Node *&root, Node *&pt);
// 右旋操作
void rotateRight(Node *&root, Node *&pt);
// 插入修复操作
void fixInsert(Node *&root, Node *&pt);
// 二叉搜索树的插入操作
Node* BSTInsert(Node* root, Node* pt);
// 更新节点的子树大小
void updateSize(Node *node);
// 查找第 k 大元素
Node* select(Node* node, int k);
public:
RedBlackTree() { root = nullptr; } // 构造函数初始化根节点为空
void insert(const int &data); // 插入操作
void inorder(); // 中序遍历
void inorderHelper(Node *root); // 中序遍历的辅助函数
Node* findKthLargest(int k); // 查找第 k 大元素
};
// 左旋操作
void RedBlackTree::rotateLeft(Node *&root, Node *&pt) {
Node *pt_right = pt->right; // 获取右子节点
pt->right = pt_right->left; // 将右子节点的左子树转移为当前节点的右子树
if (pt->right != nullptr) pt->right->parent = pt; // 更新父节点
pt_right->parent = pt->parent; // 更新右子节点的父节点
if (pt->parent == nullptr) root = pt_right; // 如果当前节点是根节点,更新根节点
else if (pt == pt->parent->left) pt->parent->left = pt_right; // 更新父节点的左子节点
else pt->parent->right = pt_right; // 更新父节点的右子节点
pt_right->left = pt; // 将当前节点设为右子节点的左子节点
pt->parent = pt_right; // 更新父节点
updateSize(pt); // 更新子树大小
updateSize(pt_right); // 更新子树大小
}
// 右旋操作
void RedBlackTree::rotateRight(Node *&root, Node *&pt) {
Node *pt_left = pt->left; // 获取左子节点
pt->left = pt_left->right; // 将左子节点的右子树转移为当前节点的左子树
if (pt->left != nullptr) pt->left->parent = pt; // 更新父节点
pt_left->parent = pt->parent; // 更新左子节点的父节点
if (pt->parent == nullptr) root = pt_left; // 如果当前节点是根节点,更新根节点
else if (pt == pt->parent->left) pt->parent->left = pt_left; // 更新父节点的左子节点
else pt->parent->right = pt_left; // 更新父节点的右子节点
pt_left->right = pt; // 将当前节点设为左子节点的右子节点
pt->parent = pt_left; // 更新父节点
updateSize(pt); // 更新子树大小
updateSize(pt_left); // 更新子树大小
}
// 插入修复操作
void RedBlackTree::fixInsert(Node *&root, Node *&pt) {
Node *parent_pt = nullptr;
Node *grand_parent_pt = nullptr;
while ((pt != root) && (pt->color != BLACK) && (pt->parent->color == RED)) {
parent_pt = pt->parent;
grand_parent_pt = pt->parent->parent;
// 父节点是祖父节点的左子节点
if (parent_pt == grand_parent_pt->left) {
Node *uncle_pt = grand_parent_pt->right;
// 情况 1:叔叔节点是红色
if (uncle_pt != nullptr && uncle_pt->color == RED) {
grand_parent_pt->color = RED;
parent_pt->color = BLACK;
uncle_pt->color = BLACK;
pt = grand_parent_pt;
} else {
// 情况 2:叔叔节点是黑色,当前节点是右子节点
if (pt == parent_pt->right) {
rotateLeft(root, parent_pt);
pt = parent_pt;
parent_pt = pt->parent;
}
// 情况 3:叔叔节点是黑色,当前节点是左子节点
rotateRight(root, grand_parent_pt);
swap(parent_pt->color, grand_parent_pt->color);
pt = parent_pt;
}
}
// 父节点是祖父节点的右子节点
else {
Node *uncle_pt = grand_parent_pt->left;
// 情况 1:叔叔节点是红色
if (uncle_pt != nullptr && uncle_pt->color == RED) {
grand_parent_pt->color = RED;
parent_pt->color = BLACK;
uncle_pt->color = BLACK;
pt = grand_parent_pt;
} else {
// 情况 2:叔叔节点是黑色,当前节点是左子节点
if (pt == parent_pt->left) {
rotateRight(root, parent_pt);
pt = parent_pt;
parent_pt = pt->parent;
}
// 情况 3:叔叔节点是黑色,当前节点是右子节点
rotateLeft(root, grand_parent_pt);
swap(parent_pt->color, grand_parent_pt->color);
pt = parent_pt;
}
}
}
root->color = BLACK; // 确保根节点是黑色
}
// 更新节点的子树大小
void RedBlackTree::updateSize(Node *node) {
if (node != nullptr) {
node->size = 1; // 当前节点计为 1
if (node->left != nullptr) {
node->size += node->left->size; // 加上左子树的大小
}
if (node->right != nullptr) {
node->size += node->right->size; // 加上右子树的大小
}
}
}
// 二叉搜索树的插入操作
Node* RedBlackTree::BSTInsert(Node* root, Node* pt) {
if (root == nullptr) return pt;
if (pt->data < root->data) {
root->left = BSTInsert(root->left, pt);
root->left->parent = root;
} else if (pt->data > root->data) {
root->right = BSTInsert(root->right, pt);
root->right->parent = root;
}
updateSize(root); // 更新子树大小
return root;
}
// 插入操作
void RedBlackTree::insert(const int &data) {
Node *pt = new Node(data); // 创建新节点
root = BSTInsert(root, pt); // 插入节点
fixInsert(root, pt); // 修复红黑树的性质
}
// 查找第 k 大元素
Node* RedBlackTree::select(Node* node, int k) {
int leftSize = (node->left != nullptr) ? node->left->size : 0; // 获取左子树的大小
int rank = leftSize + 1; // 当前节点的排名
if (k == rank) return node; // 找到第 k 大元素
else if (k < rank) return select(node->left, k); // 在左子树中查找
else return select(node->right, k - rank); // 在右子树中查找
}
// 查找第 k 大元素
Node* RedBlackTree::findKthLargest(int k) {
return select(root, k);
}
// 中序遍历
void RedBlackTree::inorder() { inorderHelper(root); }
// 中序遍历的辅助函数
void RedBlackTree::inorderHelper(Node *root) {
if (root == nullptr) return;
inorderHelper(root->left);
std::cout << root->data << " ";
inorderHelper(root->right);
}
int main() {
RedBlackTree tree;
tree.insert(10);
tree.insert(20);
tree.insert(30);
tree.insert(15);
tree.inorder();
std::cout << std::endl;
int k = 2;
Node* kthLargest = tree.findKthLargest(k);
if (kthLargest != nullptr) {
std::cout << "第 " << k << " 大的元素是 " << kthLargest->data << std::endl;
} else {
std::cout << "未找到第 " << k << " 大的元素" << std::endl;
}
return 0;
}