C++实现红黑树应用实例代码

yizhihongxing

C++实现红黑树应用实例代码

什么是红黑树

红黑树(Red-Black Tree)是一种自平衡二叉查找树,在C++中的STL中的set和map就是基于红黑树实现的。红黑树满足以下性质:

  1. 每个节点或者是黑色,或者是红色。
  2. 根节点是黑色。
  3. 每个叶子节点(NIL节点,空节点)是黑色的。
  4. 如果一个节点是红色的,则它的两个子节点都是黑色的。
  5. 对于任意一个节点而言,其到叶子节点NIL的任何路径上包含的黑色节点个数相同。

通过以上性质,可以保证红黑树的树高不超过 $2log_2n$ ,其中 $n$ 为节点个数。

C++代码实现

结构体定义

首先我们定义红黑树的节点结构体:

enum Color{ RED, BLACK };

template <class T>
struct RBTreeNode
{
    Color color;
    T key;
    RBTreeNode<T>* parent;
    RBTreeNode<T>* left;
    RBTreeNode<T>* right;

    RBTreeNode(T data, Color c = Color::RED, RBTreeNode<T>* p = nullptr,
        RBTreeNode<T>* l = nullptr, RBTreeNode<T>* r = nullptr) :
        key(data), color(c), parent(p), left(l), right(r) {}
};

我们使用枚举类型 Color 来代表节点颜色,节点中包含了当前节点的颜色、关键字信息、父节点指针、左子树指针和右子树指针。

插入节点

插入节点的过程中,我们需要通过比较关键字大小判断插入的位置。插入完成后,红黑树可能会不满足黑色节点数目相同的性质,因此我们需要对红黑树进行一系列转换,保证性质得以满足。

template <class T>
void RBTree<T>::insert(RBTreeNode<T>* node)
{
    RBTreeNode<T>* ptrNode = root;
    RBTreeNode<T>* parentNode = nullptr;

    while (ptrNode != nullptr)
    {
        parentNode = ptrNode;

        if (node->key < ptrNode->key)
        {
            ptrNode = ptrNode->left;
        }
        else
        {
            ptrNode = ptrNode->right;
        }
    }

    node->parent = parentNode;

    if (parentNode == nullptr)
    {
        root = node;
    }
    else if (node->key < parentNode->key)
    {
        parentNode->left = node;
    }
    else
    {
        parentNode->right = node;
    }

    node->color = Color::RED;

    fixupInsert(node);
}

插入修复

为了修正插入节点引起的变化,我们需要进行一些修复操作:

template <class T>
void RBTree<T>::fixupInsert(RBTreeNode<T>* node)
{
    RBTreeNode<T>* parentNode = nullptr;
    RBTreeNode<T>* grandNode = nullptr;

    while (node->parent != nullptr && node->parent->color == Color::RED)
    {
        parentNode = node->parent;
        grandNode = parentNode->parent;

        if (parentNode == grandNode->left)
        {
            RBTreeNode<T>* nodeUncle = grandNode->right;

            // 情况1
            if (nodeUncle != nullptr && nodeUncle->color == Color::RED)
            {
                parentNode->color = Color::BLACK;
                nodeUncle->color = Color::BLACK;
                grandNode->color = Color::RED;

                node = grandNode;
            }
            else
            {
                // 情况2
                if (node == parentNode->right)
                {
                    leftRotate(parentNode);
                    std::swap(node, parentNode);
                }

                // 情况3
                parentNode->color = Color::BLACK;
                grandNode->color = Color::RED;

                rightRotate(grandNode);
            }
        }
        else
        {
            RBTreeNode<T>* nodeUncle = grandNode->left;

            // 情况1
            if (nodeUncle != nullptr && nodeUncle->color == Color::RED)
            {
                parentNode->color = Color::BLACK;
                nodeUncle->color = Color::BLACK;
                grandNode->color = Color::RED;

                node = grandNode;
            }
            else
            {
                // 情况2
                if (node == parentNode->left)
                {
                    rightRotate(parentNode);
                    std::swap(node, parentNode);
                }

                // 情况3
                parentNode->color = Color::BLACK;
                grandNode->color = Color::RED;

                leftRotate(grandNode);
            }
        }
    }

    root->color = Color::BLACK;
}

左旋和右旋

左旋和右旋是红黑树的基本操作:

template <class T>
void RBTree<T>::leftRotate(RBTreeNode<T>* node)
{
    RBTreeNode<T>* rightChild = node->right;

    node->right = rightChild->left;

    if (rightChild->left != nullptr)
    {
        rightChild->left->parent = node;
    }

    rightChild->parent = node->parent;

    if (node->parent == nullptr)
    {
        root = rightChild;
    }
    else if (node == node->parent->left)
    {
        node->parent->left = rightChild;
    }
    else
    {
        node->parent->right = rightChild;
    }

    rightChild->left = node;
    node->parent = rightChild;
}

template <class T>
void RBTree<T>::rightRotate(RBTreeNode<T>* node)
{
    RBTreeNode<T>* leftChild = node->left;

    node->left = leftChild->right;

    if (leftChild->right != nullptr)
    {
        leftChild->right->parent = node;
    }

    leftChild->parent = node->parent;

    if (node->parent == nullptr)
    {
        root = leftChild;
    }
    else if (node == node->parent->left)
    {
        node->parent->left = leftChild;
    }
    else
    {
        node->parent->right = leftChild;
    }

    leftChild->right = node;
    node->parent = leftChild;
}

示例

示例一:插入节点

以插入关键字为7的节点为例,我们先构造红黑树:

            (11B)
          /      \
        (2B)     (14R)
       /    \       \
    (1R)   (7R)     (15R)

插入关键字为7的节点,红色节点:

            (11B)
          /      \
        (2B)     (14R)
       /    \       \
    (1R)   (7R)     (15R)
              \
              (7R)

插入完成后修复红黑树,得到:

            (11B)
          /      \
        (2B)     (14B)
       /    \       \
    (1R)   (7B)     (15R)
              \
              (7R)

示例二:删除节点

以删除关键字为11的节点为例,我们先构造红黑树:

            (11B)
          /      \
        (2B)     (14R)
       /    \       \
    (1R)   (7R)     (15R)

删除关键字为11的节点,得到:

            (14R)
          /      \
        (2B)     (15B)
       /    \
    (1R)   (7R)

删除完成后我们需要对红黑树进行一系列修复操作,最终得到:

            (2B)
          /      \
        (1R)     (14B)
                /    \
              (7R)   (15R)

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++实现红黑树应用实例代码 - Python技术站

(0)
上一篇 2023年5月24日
下一篇 2023年5月24日

相关文章

  • C++中的类成员函数当线程函数

    C++中的线程库(std::thread)可以处理各种类型的函数作为线程函数,包括类的成员函数。对于类成员函数,我们需要考虑如何处理this指针,并注意线程的生命周期。 以下是将类成员函数作为线程函数的完整攻略: 步骤1:定义类 首先,需要定义一个含有成员函数的类。本例中,我们定义了一个简单的Counter类,它具有公共函数increment(),用于增加计…

    C 2023年5月22日
    00
  • C语言实现文件读写

    文件读写是C语言的一个重要部分,文件读写操作主要是通过函数库提供的各种操作文件的函数来实现的。在实现文件读写时,主要分为以下几个步骤: 打开文件 C语言提供了fopen函数来打开文件,并返回一个指向文件的指针,该函数原型如下: FILE *fopen(const char *filename, const char *mode); 其中,filename表示…

    C 2023年5月23日
    00
  • C/C++如何获取当前系统时间的实例详解

    C/C++如何获取当前系统时间的实例详解 在C/C++语言中,获取当前系统时间可以通过调用系统库函数来实现。常用的获取当前系统时间的函数有time、localtime、strftime等函数。下面将详细介绍这些函数的使用方法。 time函数 time函数用来获取当前系统时间的时间戳,其函数的原型如下: #include <time.h> time…

    C 2023年5月23日
    00
  • C语言 strncat()函数

    当我们需要将一个字符串和另外一个字符串合并成一个新的字符串时,可以考虑使用C语言的strncat()函数。strncat()函数的作用就是将一个字符串的前n个字符附加到另一个字符串的末尾处,并在合并后的字符串的末尾加上字符串结束符’\0’。 strncat()函数的语法如下: char *strncat(char *dest, const char *src…

    C 2023年5月9日
    00
  • Windows上安装Apache2、PHP5、MySQL5及与Resin配合实现多系统之整合

    Windows上安装Apache2、PHP5、MySQL5及与Resin配合实现多系统之整合攻略 在Windows上安装Apache、PHP、MySQL以及与Resin进行整合,可以实现多系统之间的协同工作。本攻略将会提供详细的步骤说明,供需要的用户参考。 安装Apache2 下载Apache:官网链接 选择对应的版本下载(建议下载Windows平台下的.m…

    C 2023年5月24日
    00
  • C++ Primer Plus 第四章之C++ Primer Plus复合类型学习笔记

    C++ Primer Plus 第四章之C++ Primer Plus复合类型学习笔记 1. 复合类型简介 在C++中有许多复合类型,如数组、结构体、共用体和指针等,它们能够将多个基本类型变量组合成更加复杂的数据结构。在使用复合类型时,需要注意其内存结构和使用方法,以充分发挥其特性。 2. 数组 数组是一种复合类型,可以存储多个同一类型的数据,通过下标访问数…

    C 2023年5月22日
    00
  • thinkphp3.2同时连接两个数据库的简单方法

    想要在ThinkPHP 3.2中同时连接两个数据库,可以按照以下步骤进行: 1. 配置数据库连接参数 在ThinkPHP中,数据库连接参数是在./Application/Common/Conf/config.php文件中进行配置的。我们需要在这个文件中,将两个数据库的连接参数都进行配置。 以下是一个示例配置文件中同时连接两个MySQL数据库的配置代码: re…

    C 2023年5月23日
    00
  • C++中的函数指针与函数对象的总结

    以下是关于”C++中的函数指针与函数对象的总结”的详细攻略。 什么是函数指针? 函数指针其实就是指向函数的指针,它可以像普通指针一样进行声明、赋值、传递参数等操作。C++中的函数指针的语法形式为: 返回值类型 (*指针变量名)(参数类型列表); 举个例子,我们定义一个名为add的函数,它的作用是将两个整数相加并返回结果。那么我们可以这样声明一个函数指针变量:…

    C 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部