AVL树,Treap树,红黑树的实现(下)

简介: AVL树,Treap树,红黑树的实现

三、红黑树

红黑树讲解比较麻烦,以下的gpt对我实现的代码的总结

Red-Black Tree 通过如下的方式进行自平衡:

节点是红色或黑色。
根节点是黑色。
所有叶子节点(NIL节点,空节点)是黑色。
每个红色节点的两个子节点都是黑色(从每个叶子到根的所有路径上不能有两个连续的红色节点)。
从任一节点到其每个叶子的所有路径包含相同数目黑色节点。

具体实现时,我们可以定义 Node 类来表示红黑树的节点,其中包括节点的键、值、颜色等属性。具体的实现需要考虑以下情况:
插入节点
在插入节点时,我们需要考虑节点的颜色和位置,以保证插入后仍然满足红黑树的性质。以下是插入节点的具体实现细节:

插入到空树
当插入到空树时,我们需要创建一个根节点,将其颜色设置为黑色,并将新节点插入作为根节点的左孩子或右孩子。
插入到非空树
首先通过二叉搜索树的插入方式找到新节点要插入的位置,然后将新节点插入到该位置,并将其颜色设置为红色。接下来需要保证红黑树的性质,具体实现如下:

如果新节点的父节点为黑色,则满足红黑树的性质,无需处理。
如果新节点的父节点为红色,则需要进行变色或旋转操作,以满足红黑树的性质。

为了方便后续实现,以下称新插入的节点为 X,X 的父节点为 P,X 的祖父节点为 G,X 的叔父节点为 U。具体的情况如下:

Case 1:如果 P 为根节点,则将其颜色设置为黑色。
Case 2:如果 P 的颜色为黑色,则满足红黑树的性质,无需处理。
Case 3:如果 P 和 U 都为红色,则将 P 和 U 的颜色都设置为黑色,G 的颜色设置为红色,然后将 X 的位置设置为 G,继续向上判断。
Case 4:如果 P 为红色,U 为黑色,并且 X 是 P 的右孩子而且 P 是 G 的左孩子,或者 X 是 P 的左孩子而且 P 是 G 的右孩子,则需要进行旋转操作。具体操作如下:

如果 X 是 P 的右孩子,将 X 和 P 交换,得到情况 "X 是 P 的左孩子,P 是 G 的左孩子"。
将 P 向左旋转,得到情况 "X 是 P 的左孩子,P 是 G 的左孩子,X 的兄弟节点为 P"。
将 P 和 G 的颜色互换,得到最终的结果。

Case 5:如果 P 为红色,U 为黑色,并且 X 是 P 的左孩子而且 P 是 G 的左孩子,或者 X 是 P 的右孩子而且 P 是 G 的右孩子,则需要进行旋转操作。具体操作如下:

将 P 和 G 的颜色互换,得到情况 "X 是 P 的右孩子,P 是 G 的右孩子"。
将 P 向右旋转,得到情况 "X 是 P 的右孩子,P 是 G 的右孩子,X 的兄弟节点为 P"。
将 P 和 G 的颜色互换,得到最终的结果。

删除节点
在删除节点时,我们需要考虑节点的颜色和位置,以保证删除后仍然满足红黑树的性质。以下是删除节点的具体实现细节:

删除叶子节点
直接删除该节点,并将其父节点指向 NULL。
删除有一个子节点的节点
将该节点的子节点接到其父节点上,并删除该节点。
删除有两个子节点的节点
在右子树中找到最小的节点,将其值和键复制到要删除的节点,然后删除右子树中的最小节点。

在删除节点时,如果节点的颜色为黑色,还需要进一步处理,以保证红黑树的性质。如果删除的节点是叶子节点或只有一个子节点的节点,删除时并不会破坏红黑树的性质。如果删除的节点有两个子节点,需要找到其后继节点(即右子树中的最小节点),然后用后继节点替换要删除的节点,此时后继节点的颜色可能是红色或黑色,需要进行进一步处理,具体实现如下:

Case 1:如果后继节点是红色,则将其颜色设置为黑色。
Case 2:如果后继节点是黑色,并且其任一子节点是红色,则将其子节点的颜色设置为黑色,并将后继节点的颜色设置为红色。
Case 3:如果后继节点是黑色,并且其子节点都是黑色,则需要进行变色或旋转操作,以满足红黑树的性质。

为了方便后续实现,以下称要删除的节点为 X,X 的父节点为 P,X 的兄弟节点为 S,S 的子节点为 S1 和 S2,S1 或 S2 和 S 的颜色为红色。具体的情况如下:

Case 1:如果 X 是根节点,则将其颜色设置为黑色。
Case 2:如果 X 的兄弟节点 S 是红色,则将 S 的颜色设置为黑色,P 的颜色设置为红色,然后对 P 进行旋转操作,使 X 的兄弟节点为黑色。
Case 3:如果 X、X 的父节点 P 和 X 的兄弟节点 S 都是黑色,并且 S 的子节点都是黑色,则将 S 的颜色设置为红色,将 X 的位置设置为 P,继续向上判断。
Case 4:如果 X 的兄弟节点 S 是黑色,S 的左孩子 S1 是红色,S 的右孩子 S2 是黑色,则进行以下操作:

将 S 和 S1 的颜色互换,得到情况 "X 的兄弟节点 S 的右孩子 S2 是红色"。
将 S 向右旋转,将 S2 和 P 的颜色也进行相应的变换,得到情况 "X 的兄弟节点 S 的左孩子 S1 是黑色,S 的右孩子 S2 是红色"。
重新设置 S、S1 和 S2 的颜色,得到最终的结果。

Case 5:如果 X 的兄弟节点 S 是黑色,S 的右孩子 S2 是红色,则进行以下操作:

将 S 和 S2 的颜色互换,得到情况 "X 的兄弟节点 S 的左孩子 S1 是黑色"。
将 S 向左旋转,将 S1 和 P 的颜色也进行相应的变换,得到情况 "X 的兄弟节点 S 的左孩子 S1 是红色,S 的右孩子 S2 是黑色"。
重新设置 S、S1 和 S2 的颜色,得到最终的结果。

Case 6:如果 X 的兄弟节点 S 是黑色,S 的左孩子 S1 是红色,则进行以下操作:

将 S 和 S1 的颜色互换,得到情况 "X 的兄弟节点 S 的右孩子 S2 是黑色"。
将 S 向右旋转,将 S2 和 P 的颜色也进行相应的变换,得到情况 "X 的兄弟节点 S 的左孩子 S1 是黑色,S 的右孩子 S2 是红色"。
重新设置 S、S1 和 S2 的颜色,得到最终的结果。

以上就是 Red-Black Tree 的实现细节和各种情况的判断,希望能对您有所帮助。

以下是代码及其一些细节

#define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
// 红黑树节点的颜色,红色表示节点为2节点(即2-3树中的3节点中的左子节点或者右子节点),黑色表示节点为3节点或空节点
enum Color {
    RED, BLACK
};
// 红黑树节点的类
class RBNode {
public:
    int key;            // 节点的键
    int value;          // 节点的值
    Color color;        // 节点的颜色,红色或者黑色
    RBNode* left;       // 节点的左子节点,可以是红节点或者黑节点
    RBNode* right;      // 节点的右子节点,可以是红节点或者黑节点
    // 构造函数
    RBNode(int key, int value, Color color) :
        key(key), value(value), color(color), left(nullptr), right(nullptr) {}
};
// 红黑树的类
class RBTree {
private:
    RBNode* root;
public:
    // 构造函数
    RBTree() : root(nullptr) {}
    // 插入操作
    void insert(int key, int value) {
        root = insert_node(root, key, value);      // 调用私有的插入节点函数
        root->color = BLACK;                       // 根节点必须为黑色
    }
    // 查找操作
    int search(int key) {
        RBNode* node = search_node(root, key);     // 调用私有的查找节点函数
        if (node != nullptr) {
            return node->value;
        }
        else {
            return -1;
        }
    }
    // 删除操作
    void erase(int key) {
        root = erase_node(root, key);              // 调用私有的删除节点函数
        if (root != nullptr) {
            root->color = BLACK;                   // 根节点必须为黑色
        }
    }
    // 查询最大键值
    int max() {
        RBNode* node = max_node(root);
        if (node != nullptr) {
            return node->key;
        }
        else {
            return -1;
        }
    }
    // 查询最小键值
    int min() {
        RBNode* node = min_node(root);
        if (node != nullptr) {
            return node->key;
        }
        else {
            return -1;
        }
    }
private:
    // 左旋操作
    RBNode* left_rotate(RBNode* node) {
        RBNode* right_child = node->right;          // 获取需要左旋的节点的右子节点
        node->right = right_child->left;            // 右子节点的左子节点变成需要左旋的节点的右子节点
        right_child->left = node;                   // 需要左旋的节点变成右子节点的左子节点
        right_child->color = node->color;           // 右子节点颜色变成需要左旋的节点的颜色
        node->color = RED;                          // 需要左旋的节点变成红色
        return right_child;                         // 返回左旋后的根节点
    }
    // 右旋操作
    RBNode* right_rotate(RBNode* node) {
        RBNode* left_child = node->left;            // 获取需要右旋的节点的左子节点
        node->left = left_child->right;             // 左子节点的右子节点变成需要右旋的节点的左子节点
        left_child->right = node;                   // 需要右旋的节点变成左子节点的右子节点
        left_child->color = node->color;            // 左子节点颜色变成需要右旋的节点的颜色
        node->color = RED;                          // 需要右旋的节点变成红色
        return left_child;                          // 返回右旋后的根节点
    }
    // 颜色翻转操作
    void flip_color(RBNode* node) {
        node->color = RED;                          // 需要翻转颜色的节点变成红色
        node->left->color = BLACK;                  // 左子节点和右子节点变成黑色
        node->right->color = BLACK;
    }
    // 插入节点
    RBNode* insert_node(RBNode* node, int key, int value) {
        if (node == nullptr) {
            return new RBNode(key, value, RED);     // 如果节点是空节点,则创建一个红色节点
        }
        if (key < node->key) {
            node->left = insert_node(node->left, key, value);   // 插入到左子树
        }
        else if (key > node->key) {
            node->right = insert_node(node->right, key, value); // 插入到右子树
        }
        else {
            node->value = value;                    // 如果节点已经存在,则更新节点的值
        }
        // 实施红黑树的正常插入操作
        if (is_red(node->right) && !is_red(node->left)) {      // 如果需要右旋
            node = left_rotate(node);
        }
        if (is_red(node->left) && is_red(node->left->left)) {  // 如果需要左旋
            node = right_rotate(node);
        }
        if (is_red(node->left) && is_red(node->right)) {       // 如果需要颜色翻转
            flip_color(node);
        }
        return node;
    }
    // 删除节点
    RBNode* erase_node(RBNode* node, int key) {
        if (node == nullptr) {
            return nullptr;
        }
        if (key < node->key) {
            node->left = erase_node(node->left, key);     // 删除左子树的节点
        }
        else if (key > node->key) {
            node->right = erase_node(node->right, key);   // 删除右子树的节点
        }
        else {
            if (node->left == nullptr) {
                RBNode* right_child = node->right;
                delete node;
                return right_child;
            }
            if (node->right == nullptr) {
                RBNode* left_child = node->left;
                delete node;
                return left_child;
            }
            RBNode* replace_node = min_node(node->right);    // 找到右子树中的最小节点
            node->key = replace_node->key;                  // 用最小节点替换需要删除的节点
            node->value = replace_node->value;
            node->right = erase_min(node->right);           // 删除替换节点,即删除右子树中的最小节点
        }
        // 实施红黑树的删除后的修正操作
        if (is_red(node->right) && !is_red(node->left)) {      // 如果需要右旋和颜色翻转
            node = left_rotate(node);
        }
        if (is_red(node->left) && is_red(node->left->left)) {  // 如果需要左旋和颜色翻转
            node = right_rotate(node);
        }
        if (is_red(node->left) && is_red(node->right)) {       // 如果需要颜色翻转
            flip_color(node);
        }
        return node;
    }
    // 删除最小键值节点
    RBNode* erase_min(RBNode* node) {
        if (node->left == nullptr) {
            RBNode* right_child = node->right;
            delete node;
            return right_child;
        }
        if (!is_red(node->left) && !is_red(node->left->left)) {  // 如果左子节点为黑色且左子节点的左子节点为黑色,则进行颜色翻转
            node = move_red_left(node);
        }
        node->left = erase_min(node->left);     // 递归删除左子树的最小键值节点
        return balance(node);                   // 做一次平衡操作
    }
    // 将左子树的红节点移到右子树中
    RBNode* move_red_left(RBNode* node) {
        flip_color(node);                       // 颜色翻转
        if (is_red(node->right->left)) {        // 如果右子节点的左子节点为红色,则右旋
            node->right = right_rotate(node->right);
            node = left_rotate(node);
            flip_color(node);
        }
        return node;
    }
    // 查询节点
    RBNode* search_node(RBNode* node, int key) {
        if (node == nullptr) {
            return nullptr;                     // 节点不存在,则返回空指针
        }
        if (key < node->key) {
            return search_node(node->left, key); // 继续在左子树中查找
        }
        else if (key > node->key) {
            return search_node(node->right, key);// 继续在右子树中查找
        }
        else {
            return node;                         // 找到节点,则返回指向该节点的指针
        }
    }
    // 查询最大节点
    RBNode* max_node(RBNode* node) {
        if (node->right == nullptr) {           // 如果节点的右子树为空,则该节点为最大节点
            return node;
        }
        else {
            return max_node(node->right);       // 继续在右子树中查找
        }
    }
    // 查询最小节点
    RBNode* min_node(RBNode* node) {
        if (node->left == nullptr) {            // 如果节点的左子树为空,则该节点为最小节点
            return node;
        }
        else {
            return min_node(node->left);        // 继续在左子树中查找
        }
    }
    // 平衡操作,保证每个节点左右子树中黑节点的数量相等
    RBNode* balance(RBNode* node) {
        if (is_red(node->right)) {                      // 如果右子节点为红色,则左旋
            node = left_rotate(node);
        }
        if (is_red(node->left) && is_red(node->left->left)) {  // 如果左子节点为红色且左子节点的左子节点为红色,则右旋
            node = right_rotate(node);
        }
        if (is_red(node->left) && is_red(node->right)) {       // 如果左右子节点都为红色,则进行颜色翻转
            flip_color(node);
        }
        return node;                                    // 返回平衡后的根节点
    }
    // 判断节点的颜色是否为红色,如果节点为nullptr,则其颜色为黑色
    bool is_red(RBNode* node) {
        return (node != nullptr && node->color == RED);
    }
};
int main() {
    RBTree tree;
    tree.insert(3, 5);   // 插入键值为3,值为5的节点
    tree.insert(1, 10);  // 插入键值为1,值为10的节点
    tree.insert(5, 7);   // 插入键值为5,值为7的节点
    cout << tree.search(3) << endl;  // 查询键值为3的节点的值,输出5
    cout << tree.search(2) << endl;  // 查询键值为2的节点的值,输出-1,表示未找到
    cout << tree.max() << endl;      // 查询红黑树中最大的键的值,输出5
    cout << tree.min() << endl;      // 查询红黑树中最小的键的值
}
相关文章
|
算法
AVL树,Treap树,红黑树的实现(上)
AVL树,Treap树,红黑树的实现
|
1月前
|
存储 C++
【C++】AVL树
AVL树是一种自平衡二叉搜索树,由Georgy Adelson-Velsky和Evgenii Landis提出。它通过确保任意节点的两子树高度差不超过1来维持平衡,支持高效插入、删除和查找操作,时间复杂度为O(log n)。AVL树通过四种旋转操作(左旋、右旋、左-右旋、右-左旋)来恢复树的平衡状态,适用于需要频繁进行数据操作的场景。
46 2
|
7月前
AVL 树
AVL 树
60 2
|
7月前
|
C++ 容器
【C++】—— 详解AVL树
【C++】—— 详解AVL树
|
7月前
|
存储 测试技术 C++
C++【AVL树】
C++【AVL树】
84 0
二叉搜索树之AVL树
二叉搜索树之AVL树
|
C++
C++实现AVL树
C++实现AVL树
69 0
|
算法 Java Python
实现AVL树
大家好,我是王有志。今天,我们会用Java和Python分别实现第一个平衡二分搜索树--AVL树。
88 0
实现AVL树
|
算法
平衡二叉树(AVL树)
平衡二叉树(AVL树)
88 0
|
测试技术 C++ Perl
C++之AVL树(下)
C++之AVL树(下)
79 0