红黑树(Red-Black Tree)是一种自平衡的二叉查找树(BST),它在插入、删除和查找操作后通过一些特定的规则来维护树的平衡,从而确保这些操作的时间复杂度始终为O(log n)。红黑树主要应用在需要高效动态集合操作的场景中,如操作系统中的进程调度器、数据库中的索引等。
红黑树的基本性质
红黑树的每个节点都有一个颜色属性,可以是红色或黑色。红黑树通过以下性质来维持平衡:
- 节点颜色:每个节点都是红色或黑色。
- 根节点:根节点必须是黑色的。
- 叶节点:每个叶节点(通常是空节点,表示
nullptr
)是黑色的。 - 红色节点的限制:红色节点不能有红色的子节点,即红色节点的子节点必须是黑色的。这确保了不会出现连续的红色节点。
- 黑色节点的平衡:从任意节点到其每个叶节点的所有路径上必须包含相同数量的黑色节点。
这些性质确保了树的高度近似平衡,从而保证了在最坏情况下,树的高度为O(log n)。
操作介绍
-
插入:在红黑树中插入节点与在普通二叉查找树中插入类似,但插入后需要通过重新着色和旋转来恢复红黑树的平衡性。插入操作保证了红黑树的高度不会变得过高。
-
删除:删除节点后的树可能会违反红黑树的性质,因此删除操作通常需要进行多次旋转和重新着色,以恢复树的平衡性。
-
查找:红黑树的查找操作与普通二叉查找树相同,通过比较键值,沿树路径查找目标节点。
优势与应用
-
时间复杂度稳定:由于红黑树的自平衡特性,插入、删除和查找的时间复杂度始终为O(log n),这使得红黑树在许多需要频繁动态操作的应用中非常有效。
-
动态调整:红黑树能够动态调整其结构,使其适用于一系列需要频繁插入和删除操作的场景,例如实时系统中的任务调度、编译器中的符号表管理等。
-
平衡性:红黑树保持了树的高度平衡,使得在大量数据的处理和访问过程中,操作时间不会因树的高度而显著增加。
红黑树的应用
- C++ STL 的
map
和set
:C++的标准模板库中的map
和set
容器通常使用红黑树作为底层数据结构来实现。 - 操作系统中的调度器:许多操作系统的任务调度器使用红黑树来管理任务队列,以保证任务的高效查找和插入。
- 数据库索引:红黑树经常用于数据库中的索引结构,以便在大量数据中快速查找和维护索引。
红黑树代码实现
#include <iostream>
#include <queue>
using namespace std;
enum Color { RED, BLACK };
struct Node {
int data;
Color color;
Node *left, *right, *parent;
Node(int data) : data(data) {
parent = left = right = nullptr;
color = RED; // 新插入的节点是红色
}
};
class RedBlackTree {
private:
Node *root;
void rotateLeft(Node *&);
void rotateRight(Node *&);
void fixInsert(Node *&);
void fixDelete(Node *&);
void inorderHelper(Node *node) const;
void levelOrderHelper(Node *node) const;
Node* minValueNode(Node* node);
void deleteNodeHelper(Node* node, int key);
public:
RedBlackTree() { root = nullptr; }
void insert(int data);
void deleteNode(int data);
Node* search(int data);
int extractMin();
void inorder() const;
void levelOrder() const;
};
void RedBlackTree::rotateLeft(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;
}
void RedBlackTree::rotateRight(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;
}
void RedBlackTree::fixInsert(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;
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 {
if (pt == parent_pt->right) {
rotateLeft(parent_pt);
pt = parent_pt;
parent_pt = pt->parent;
}
rotateRight(grand_parent_pt);
swap(parent_pt->color, grand_parent_pt->color);
pt = parent_pt;
}
} else {
Node *uncle_pt = grand_parent_pt->left;
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 {
if (pt == parent_pt->left) {
rotateRight(parent_pt);
pt = parent_pt;
parent_pt = pt->parent;
}
rotateLeft(grand_parent_pt);
swap(parent_pt->color, grand_parent_pt->color);
pt = parent_pt;
}
}
}
root->color = BLACK;
}
void RedBlackTree::insert(int data) {
Node *pt = new Node(data);
if (root == nullptr) {
pt->color = BLACK;
root = pt;
} else {
Node *current = root;
Node *parent = nullptr;
while (current != nullptr) {
parent = current;
if (pt->data < current->data)
current = current->left;
else
current = current->right;
}
pt->parent = parent;
if (pt->data < parent->data)
parent->left = pt;
else
parent->right = pt;
fixInsert(pt);
}
}
void RedBlackTree::fixDelete(Node *&x) {
while (x != root && x->color == BLACK) {
if (x == x->parent->left) {
Node *w = x->parent->right;
if (w->color == RED) {
w->color = BLACK;
x->parent->color = RED;
rotateLeft(x->parent);
w = x->parent->right;
}
if (w->left->color == BLACK && w->right->color == BLACK) {
w->color = RED;
x = x->parent;
} else {
if (w->right->color == BLACK) {
w->left->color = BLACK;
w->color = RED;
rotateRight(w);
w = x->parent->right;
}
w->color = x->parent->color;
x->parent->color = BLACK;
w->right->color = BLACK;
rotateLeft(x->parent);
x = root;
}
} else {
Node *w = x->parent->left;
if (w->color == RED) {
w->color = BLACK;
x->parent->color = RED;
rotateRight(x->parent);
w = x->parent->left;
}
if (w->right->color == BLACK && w->left->color == BLACK) {
w->color = RED;
x = x->parent;
} else {
if (w->left->color == BLACK) {
w->right->color = BLACK;
w->color = RED;
rotateLeft(w);
w = x->parent->left;
}
w->color = x->parent->color;
x->parent->color = BLACK;
w->left->color = BLACK;
rotateRight(x->parent);
x = root;
}
}
}
x->color = BLACK;
}
Node* RedBlackTree::minValueNode(Node* node) {
Node* current = node;
while (current->left != nullptr)
current = current->left;
return current;
}
void RedBlackTree::deleteNodeHelper(Node* root, int data) {
Node *z = nullptr;
Node *x, *y;
while (root != nullptr) {
if (root->data == data)
z = root;
if (root->data <= data)
root = root->right;
else
root = root->left;
}
if (z == nullptr)
return;
y = z;
Color y_original_color = y->color;
if (z->left == nullptr) {
x = z->right;
if (x != nullptr)
x->parent = z->parent;
if (z->parent == nullptr)
root = x;
else if (z == z->parent->left)
z->parent->left = x;
else
z->parent->right = x;
delete z;
} else if (z->right == nullptr) {
x = z->left;
if (x != nullptr)
x->parent = z->parent;
if (z->parent == nullptr)
root = x;
else if (z == z->parent->left)
z->parent->left = x;
else
z->parent->right = x;
delete z;
} else {
y = minValueNode(z->right);
y_original_color = y->color;
x = y->right;
if (y->parent == z) {
if (x != nullptr)
x->parent = y;
} else {
if (x != nullptr)
x->parent = y->parent;
y->parent->left = x;
y->right = z->right;
if (y->right != nullptr)
y->right->parent = y;
}
if (z->parent == nullptr)
root = y;
else if (z == z->parent->left)
z->parent->left = y;
else
z->parent->right = y;
y->parent = z->parent;
y->color = z->color;
y->left = z->left;
if (y->left != nullptr)
y->left->parent = y;
delete z;
}
if (y_original_color == BLACK)
fixDelete(x);
}
void RedBlackTree::deleteNode(int data) {
deleteNodeHelper(root, data);
}
Node* RedBlackTree::search(int data) {
Node* current = root;
while (current != nullptr
) {
if (current->data == data)
return current;
else if (data < current->data)
current = current->left;
else
current = current->right;
}
return nullptr;
}
int RedBlackTree::extractMin() {
if (root == nullptr)
return -1;
Node* minNode = minValueNode(root);
int minValue = minNode->data;
deleteNode(minValue);
return minValue;
}
void RedBlackTree::inorderHelper(Node *node) const {
if (node == nullptr)
return;
inorderHelper(node->left);
cout << node->data << " ";
inorderHelper(node->right);
}
void RedBlackTree::inorder() const {
inorderHelper(root);
}
void RedBlackTree::levelOrderHelper(Node *node) const {
if (node == nullptr)
return;
queue<Node *> q;
q.push(node);
while (!q.empty()) {
Node *temp = q.front();
cout << temp->data << " ";
q.pop();
if (temp->left != nullptr)
q.push(temp->left);
if (temp->right != nullptr)
q.push(temp->right);
}
}
void RedBlackTree::levelOrder() const {
levelOrderHelper(root);
}
int main() {
RedBlackTree tree;
tree.insert(7);
tree.insert(3);
tree.insert(18);
tree.insert(10);
tree.insert(22);
tree.insert(8);
tree.insert(11);
tree.insert(26);
cout << "Inorder traversal of the tree:\n";
tree.inorder();
cout << "\nLevel order traversal of the tree:\n";
tree.levelOrder();
cout << "\n\nAfter deleting 18, 11, and 3:\n";
tree.deleteNode(18);
tree.deleteNode(11);
tree.deleteNode(3);
cout << "Inorder traversal of the modified tree:\n";
tree.inorder();
cout << "\nLevel order traversal of the modified tree:\n";
tree.levelOrder();
cout << "\n\nSearch for 10: ";
Node* searchResult = tree.search(10);
if (searchResult != nullptr)
cout << "Found\n";
else
cout << "Not Found\n";
cout << "\nExtracting minimum: " << tree.extractMin() << endl;
cout << "Inorder traversal after extracting min:\n";
tree.inorder();
cout << endl;
return 0;
}
使用示例及实例测试步骤
- 插入元素:向树中插入多个整数,如7, 3, 18, 10, 22, 8, 11, 26。
- 删除元素:删除某些特定的元素,如18, 11, 3。
- 查找元素:查找特定的元素,如10。
- 取出最小值:提取树中的最小值,并在提取后查看树的结构。
- 遍历:通过中序遍历(Inorder)和层序遍历(Level Order)来查看树的结构。
实例测试结果
Inorder traversal of the tree:
3 7 8 10 11 18 22 26
Level order traversal of the tree:
7 3 18 8 22 10 26 11
After deleting 18, 11, and 3:
Inorder traversal of the modified tree:
7 8 10 22 26
Level order traversal of the modified tree:
7 8 22 10 26
Search for 10: Found
Extracting minimum: 7
Inorder traversal after extracting min:
8 10 22 26
代码解析
-
节点结构:
Node
结构体包含数据、颜色以及指向左右子节点和父节点的指针。节点颜色用于维持红黑树的平衡。 -
插入操作:
insert
方法在插入节点后,通过fixInsert
方法进行必要的旋转和重新着色,确保红黑树的性质不被破坏。 -
删除操作:
deleteNode
方法删除节点后,通过fixDelete
方法进行旋转和重新着色,以恢复红黑树的平衡。 -
查找操作:
search
方法遍历树以查找特定的值。 -
取出最小值:
extractMin
方法找到最小的节点并将其删除。 -
遍历:中序遍历用于输出树中的元素,层序遍历用于展示树的层次结构。
红黑树与其他数据结构的优势
-
与普通二叉查找树(BST)相比:红黑树通过自平衡机制,确保了最坏情况下的时间复杂度为O(log n),而普通的二叉查找树在最坏情况下的时间复杂度可能退化为O(n)。
-
与AVL树相比:红黑树在插入和删除时的旋转次数比AVL树更少,因此在插入和删除操作较频繁的情况下,红黑树的性能往往优于AVL树。
-
与哈希表相比:虽然哈希表的查找、插入和删除操作的平均时间复杂度为O(1),但红黑树维护了元素的有序性,适合需要有序遍历元素的场景。