红-黑树——C++实现

本文介绍了红-黑树的基本概念,包括其性质和颜色规则,并详细阐述了红-黑树的C++实现,包括节点定义、插入和删除操作。插入时,通过分析节点颜色和位置,对六种不平衡情况进行处理。删除操作则根据删除节点的颜色和兄弟节点的情况,调整颜色和旋转以恢复平衡。文章最后给出了完整的C++源码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

红-黑树基本概念



红-黑树(Red-Black tree)是一种特殊的二叉查找树,因此具有二叉查找树的特征:任意一个节点所包含的关键字,大于左孩子的关键字,小于右孩子的关键字。树中每一个节点的颜色或者是黑色或者是红色,和AVL树一样具有平衡性。每一个空指针用一个外部结点来替代,红-黑树的性质可以描述为:

RB1:根节点和外部节点都是黑色。
RB2:在根至外部节点路径上,没有连续两个节点是红色。
RB3:在所有根至外部节点的路径上,黑色节点的数目都相同。

还有一种描述,其取决于父子指针的颜色,由父节点指向红色孩子的指针颜色为红,由父节点指向黑色儿子的指针颜色为黑。所以上述性质可以等价描述为:

RB1’:从内部节点指向外部节点的指针是黑色的。
RB2’:在跟至外部节点路径上,没有连续两个指针式红色的。
RB3’:在所有根至外部节点的路径上,黑色指针的数目都相同。

红-黑树示意图如下:
红黑树示意图

红-黑树的C++实现



对于节点颜色和指针颜色的性质描述,我们以下的讨论只针对节点颜色,相应的指针的颜色变化很容易推断出来。

1、红-黑树的节点

#pragma once

enum RBTreeColor{RED,BLACK};

template <typename T>
struct RBTreeNode
{
    RBTreeColor color;
    T key;
    RBTreeNode<T>* leftChild;
    RBTreeNode<T>* rightChild;
    RBTreeNode<T>* parent;

    RBTreeNode(T key, RBTreeColor c, RBTreeNode<T>* l, 
        RBTreeNode<T>* r, RBTreeNode<T>* p) :
        key(value), color(c), leftChild(l), rightChild(r), parent(p) { }
    RBTreeNode(T key, RBTreeColor c) : key(key), color(c), 
        leftChild(nullptr), rightChild(nullptr), parent(nullptr) { }
};

数据成员说明:
color:节点的颜色
key:关键字
leftChild:节点的左孩子
rightChild:节点的右孩子
parent:节点的父亲

使用enum定义了一个枚举类型对象,节点要么是黑色,要么是红色。

2、红-黑树类的接口

//***RBTree.h
#pragma once
#include<iostream>
#include"RBTreeNode.h"

using namespace std;

template <typename T>
class RBTree
{
public:
    RBTree() : root(nullptr) { }
    ~RBTree() { destroy(root); }

    // 前序、中序、后序遍历红-黑树
    void preOrder() { preOrder(root); }
    void inOrder() { inOrder(root); }
    void postOrder() { postOrder(root); }

    RBTreeNode<T>* find(const T& theKey) const;

    // 查找最小和最大结点:返回结点的关键字指针。
    T* minimum();
    T* maximum();

    // 将结点(theKey为节点关键字)插入到红黑树中
    void insert(const T& theKey);

    // 删除结点(theKey为节点关键字)
    void erase(const T& theKey);

    // 销毁红黑树
    void destroy();

    void output() { inOrder(root); cout << endl; }
private:
    RBTreeNode<T>* root;

    // 前序、中序、后序遍历红-黑树
    void preOrder(RBTreeNode<T>* theRoot) const;
    void inOrder(RBTreeNode<T>* theRoot) const;
    void postOrder(RBTreeNode<T>* theRoot) const;

    // 查找最小结点:返回以theRoot为根结点的红-黑树的最小结点
    RBTreeNode<T>* minimum(RBTreeNode<T>* theRoot);
    // 查找最大结点:返回以theRoot为根结点的红-黑树的最大结点
    RBTreeNode<T>* maximum(RBTreeNode<T>* theRoot);

    // 左旋
    void rightRotate(RBTreeNode<T>* gu);
    // 右旋
    void leftRotate(RBTreeNode<T>* gu);
    // 插入修正函数
    void insertFixUp(RBTreeNode<T>* u);
    // 删除修正函数
    void eraseFixUp(RBTreeNode<T> *y, RBTreeNode<T> *py);

    // 销毁红黑树
    void destroy(RBTreeNode<T>* &theRoot);

};

对于插入和删除操作,首先和二叉查找树一样进行插入和删除操作,若造成红-黑树不平衡,再用insertFixUp()和eraseFixUp()进行修正。

3、旋转操作

当红-黑树失去平衡时,需要进行颜色变化和选择操作,类似于AVL的旋转,分为左旋和右旋两种:

leftRotate:

/*********************************
***leftRotate:
*       gu                  pu
*      /  \                /  \
*     guL pu ---->        gu   puR
*        /  \            /  \
*       puL  puR       guL  puL
**********************************/
template <typename T>
void RBTree<T>::leftRotate(RBTreeNode<T>* gu)
{
    RBTreeNode<T>* pu = gu->rightChild;

    gu->rightChild = pu->leftChild;
    if (pu->leftChild != nullptr)
        pu->leftChild->parent = gu;

    pu->parent = gu->parent;
    if (gu == root)
        root = gu;
    else
    {
        if (gu == gu->parent->leftChild)
            gu->parent->leftChild = pu;
        else
            gu->parent->rightChild = pu;
    }

    pu->leftChild = gu;
    gu->parent = pu;
}

rightRotate:

/*********************************
***rightRotate:
*       gu                  pu
*      /  \                /  \ 
*     pu  guR ---->      puL   gu
*    /  \                     /  \ 
*   puL  puR                puR  guR
**********************************/
template <typename T>
void RBTree<T>::rightRotate(RBTreeNode<T>* gu)
{
    RBTreeNode<T>* pu = gu->leftChild;

    gu->leftChild = pu->rightChild;
    if (pu->rightChild != nullptr)
        pu->rightChild->parent = gu;

    pu->parent = gu->parent;
    if (gu == root)
        root = pu;
    else
    {
        if (gu == gu->parent->leftChild)
            gu->parent->leftChild = pu;
        else
            gu->parent->rightChild = pu;
    }

    pu->rightChild = gu;    
    gu->parent = pu;
}

4、插入

将一个节点插入到红-黑树中,和二叉查找树一样,但是因为节点是有颜色的,所以可能导致树失去平衡。如果插入的是“黑色”节点,将一定违反RB3(根到外部节点的黑色节点数目不等),而如果插入的是“红色”节点,有可能违反RB2(连续两个红色节点),也有可能不违反。所以,我们将新插入的节点染成“红色”,按照二叉查找树的方法插入到红-黑树中,若失去平衡,再进行修正。

插入:

template <typename T>
void RBTree<T>::insert(const T& theKey)
{
    //创建新节点,并着色为“红色”
    RBTreeNode<T>* newNode =
        new RBTreeNode<T>(theKey, RED);
    if (newNode == nullptr)
        return;

    RBTreeNode<T>* p = root,
        *pp = nullptr;

    while (p != nullptr)
    {
        pp = p;
        if (theKey < p->key)
            p = p->leftChild;
        else if (theKey > p->key)
            p = p->rightChild;
        else
        {
            cerr << "不允许添加相同的节点!" << endl;
            return;
        }
    }

    newNode->parent = pp;
    if (pp != nullptr)
    {
        if (theKey < pp->key)
            pp->leftChild = newNode;
        else
            pp->rightChild = newNode;
    }
    else
        root = newNode;

    //修正添加newNode造成的红-黑树失去平衡
    insertFixUp(newNode);
}

不平衡的类型可以通过检查新节点u、其父节点pu及其祖父节点gu来确定。RB2性质被破坏时,即连续两个红色节点:一个是u,另一个必定是它的父节点pu,因此pu存在。根据u的叔叔节点的颜色和父节点的位置,可以分为六种情况:

a、父节点是左孩子,叔叔节点是红色, 对应LLr或LRr,只需要进行颜色变化。将pu和guR变成“黑色”,gu变成“红色”。若gu的父节点存在且为“红色”,将转变成a、b、c、d、e或f情况,继续迭代。
LLr或LRr
代码:

if (guR && guR->color == RED)
{
    pu->color = BLACK;
    guR->color = BLACK;
    gu->color<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值