【C++】----红黑树

红黑树可以看作是优化过的二叉搜索树,在插入大量数据的情况下,整体效率优于AVL树。同时C++ STL库中的map和set底层结构是用红黑树实现的,这篇文章讲解红黑树的特性及原理。


一、红黑树的概念

1.1 概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

1.2 性质

1. 每个结点不是红色就是黑色

2. 根节点是黑色的

3. 如果一个节点是红色的,则它的两个孩子结点是黑色的

4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点

5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)


二、红黑树实现

2.1 红黑树的节点

红黑树节点中包含:颜色(红or黑)、数据(val)、三叉链。

定义颜色

enum Colour
{
    RED,
    BLACK
};

定义节点

// 节点
template <class K, class V>
struct RBTreeNode
{
    RBTreeNode<K, V>* _parent;
    RBTreeNode<K, V>* _left;
    RBTreeNode<K, V>* _right;

    pair<K, V> _kv;
    Colour _col;

    RBTreeNode(const pair<K, V> kv)
        : _parent(nullptr)
        , _left(nullptr)
        , _right(nullptr)
        , _kv(kv)
    {}
};

2.2 红黑树的插入

红黑树的插入分为两步:

  1. 按照搜索树的规则插入新节点

  1. 检测新节点插入后,红黑树的性质是否造到破坏

template<class ValueType>
class RBTree
{
    //……
    bool Insert(const ValueType& data)
    {
        PNode& pRoot = GetRoot();
        if (nullptr == pRoot)
        {
            pRoot = new Node(data, BLACK);
            // 根的双亲为头节点
            pRoot->_pParent = _pHead;
            _pHead->_pParent = pRoot;
        }
        else
        {
            // 1. 按照二叉搜索的树方式插入新节点
                        // 2. 检测新节点插入后,红黑树的性质是否造到破坏,
            //   若满足直接退出,否则对红黑树进行旋转着色处理
        }
        // 根节点的颜色可能被修改,将其改回黑色
        pRoot->_color = BLACK;
        _pHead->_pLeft = LeftMost();
        _pHead->_pRight = RightMost();
        return true;
    }

private:
    PNode& GetRoot() { return _pHead->_pParent; }
    // 获取红黑树中最小节点,即最左侧节点
    PNode LeftMost();
    // 获取红黑树中最大节点,即最右侧节点
    PNode RightMost();

private:
    PNode _pHead;
};

2.3 红黑树的旋转

新节点的默认颜色是红色,如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:

(约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点)

bool Insert(const ValueType& data)
{
    // ...
    // 新节点插入后,如果其双亲节点的颜色为空色,则违反性质3:不能有连在一起的红色结点
        while (pParent && RED == pParent->_color)
        {
            // 注意:grandFather一定存在
            // 因为pParent存在,且不是黑色节点,则pParent一定不是根,则其一定有双亲
            PNode grandFather = pParent->_pParent;
            // 先讨论左侧情况
            if (pParent == grandFather->_pLeft)
            {
                PNode unclue = grandFather->_pRight;
                // 情况三:叔叔节点存在,且为红
                if (unclue && RED == unclue->_color)
                {
                    pParent->_color = BLACK;
                    unclue->_color = BLACK;
                    grandFather->_color = RED;
                    pCur = grandFather;
                    pParent = pCur->_pParent;
                }
                else
                {
                    // 情况五:叔叔节点不存在,或者叔叔节点存在且为黑
                    if (pCur == pParent->_pRight)
                    {
                        _RotateLeft(pParent);
                        swap(pParent, pCur);
                    }
                    // 情况五最后转化成情况四
                    grandFather->_color = RED;
                    pParent->_color = BLACK;
                    _RotateRight(grandFather);
                }
            }
            else
            {
                // ..........
            }
        }
    // ...
}
2.3.1 cur为红,p为红,g为黑,u存在且为红

将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。

2.3.2 cur为红,p为红,g为黑,u不存在/u存在且为黑 ,cur = p->_left

u的情况有两种:

  1. u节点不存在,则cur一定是新插入的节点,因为如果cur不是新插入的节点,则cur和p一定有一个节点的颜色是黑色,就不满足性质4:每条路径包含相同的黑色节点。

  1. 如果u节点存在,则其一定是黑色的那么cur节点原来的颜色一定是黑色的,现在看到其是红色的原因是因为cur的子树在调整的过程中将cur节点的颜色由黑色改为红色。

p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,

p为g的右孩子,cur为p的右孩子,则进行左单旋转

p、g变色--p变黑,g变红

2.3.3 cur为红,p为红,g为黑,u不存在/u存在且为黑, cur = p->_right

p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,

p为g的右孩子,cur为p的左孩子,则针对p做右单旋转

则转换成了情况2

2.4 红黑树的验证

红黑树的检测分为两步:

1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)

2. 检测其是否满足红黑树的性质

bool IsValidRBTree()
{
    PNode pRoot = GetRoot();
    // 空树也是红黑树
    if (nullptr == pRoot)
        return true;
    // 检测根节点是否满足情况
    if (BLACK != pRoot->_color)
    {
        cout << "违反红黑树性质二:根节点必须为黑色" << endl;
        return false;
    }
    // 获取任意一条路径中黑色节点的个数
    size_t blackCount = 0;
    
    PNode pCur = pRoot;
    while (pCur)
    {
        if (BLACK == pCur->_color)
            blackCount++;
        pCur = pCur->_pLeft;
    }
    // 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
    size_t k = 0;
    return _IsValidRBTree(pRoot, k, blackCount);
}



bool _IsValidRBTree(PNode pRoot, size_t k, const size_t blackCount)
{
    //走到null之后,判断k和black是否相等
    if (nullptr == pRoot)
    {
        if (k != blackCount)
        {
            cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
            return false;
        }
        return true;
    }

    // 统计黑色节点的个数
    if (BLACK == pRoot->_color)
        k++;
    // 检测当前节点与其双亲是否都为红色
    PNode pParent = pRoot->_pParent;

    if (pParent && RED == pParent->_color && RED == pRoot->_color)
    {
        cout << "违反性质三:没有连在一起的红色节点" << endl;
        return false;
    }

    return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&
        _IsValidRBTree(pRoot->_pRight, k, blackCount);
}

2.5 红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O($log_2 N$),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

三、完整代码

RedBlackTree.h

#pragma once

enum Colour
{
    RED,
    BLACK
};



// 节点
template <class K, class V>
struct RBTreeNode
{
    RBTreeNode<K, V>* _parent;
    RBTreeNode<K, V>* _left;
    RBTreeNode<K, V>* _right;

    pair<K, V> _kv;
    Colour _col;

    RBTreeNode(const pair<K, V> kv)
        : _parent(nullptr)
        , _left(nullptr)
        , _right(nullptr)
        , _kv(kv)
    {}
};




// 红黑树
template<class K, class V>
struct RBTree
{
    typedef RBTreeNode<K, V> Node;
public:
    bool Insert(const pair<K, V>& kv)
    {
        if (_root == nullptr)
        {
            _root = new Node(kv);
            _root->_col = BLACK;
            return true;
        }

        Node* parent = nullptr;
        Node* cur = _root;
        while (cur)
        {
            if (cur->_kv.first > kv.first)
            {
                parent = cur;
                cur = cur->_left;
            }
            else if (cur->_kv.first < kv.first)
            {
                parent = cur;
                cur = cur->_right;
            }
            else
            {
                return false;
            }
        }

        cur = new Node(kv);
        cur->_col = RED;
        
        if (parent->_kv.first < kv.first)
        {
            parent->_right = cur;
        }
        else
        {
            parent->_left = cur;
        }

        cur->_parent = parent;

        while (parent&& parent->_col == RED)
        {
            Node* grandfather = parent->_parent;
            assert(grandfather && grandfather->_col == BLACK);

            // 通过uncle分出不同情况
            if (parent == grandfather->_left)
            {
                Node* uncle = grandfather->_right;
                // 情况一 parent uncle都存在且都为红
                // 变色处理
                if (uncle && uncle->_col == RED)
                {
                    grandfather->_col = RED;
                    parent->_col = BLACK;
                    uncle->_col = BLACK;

                    cur = grandfather;
                    parent = cur->_parent;
                }
                //情况二+三 parent存在且为红,uncle存在且为黑   parent存在 unlce不存在
                else
                {
                    if (cur == parent->_left)
                    {
                        // 情况二
                        // 右单旋+变色
                        RotateR(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else
                    {
                        // 情况三
                        // 左右单旋+变色
                        RotateL(parent);
                        RotateR(grandfather);
                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }

                    break;
                }

            }
            else
            {
                Node* uncle = grandfather->_right;
                // 情况一 parent uncle都存在且都为红
                // 变色处理
                if (uncle && uncle->_col == RED)
                {
                    grandfather->_col = RED;
                    parent->_col = BLACK;
                    uncle->_col = BLACK;

                    cur = grandfather;
                    parent = cur->_parent;
                }
                //情况二+三 parent存在且为红,uncle存在且为黑   parent存在 unlce不存在
                else
                {
                    if (cur == parent->_left)
                    {
                        // 情况二
                        // 左单旋+变色
                        RotateL(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else
                    {
                        // 情况三
                        // 右左单旋+变色
                        RotateR(parent);
                        RotateL(grandfather);
                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }

                    break;
                }
            }

        }

        _root->_col = BLACK;
        return true;
    }

    void InOrder()
    {
        _InOrder(_root);
        cout << endl;
    }


    bool IsBalance()
    {
        if (_root == nullptr)
        {
            return true;
        }

        if (_root->_col == RED)
        {
            cout << "根节点不是黑色!!!" << endl;
            return false;
        }

        // 黑色节点基准值
        int benchmark = 0;

        return _PrevCheck(_root, 0, benchmark);
    }

private:
    // 中序遍历
    void _InOrder(const Node* root)
    {
        if (root == nullptr)
        {
            return;
        }

        _InOrder(root->_left);
        cout << root->_kv.first << " : " << root->_kv.second << endl;
        _InOrder(root->_right);
    }

    // 检查
    bool _PrevCheck(Node* root, int blacknum, int& benchmark)
    {
        if (root == nullptr)
        {
            if (benchmark == 0)
            {
                benchmark = blacknum;
                return true;
            }

            if (blacknum != benchmark) // 某条路径上黑色节点数目不一样
            {
                cout << "某条路径上黑色节点数目不一样" << endl;
                return false;
            }
            else
            {
                return true;
            }

        }

        if (root->_col = BLACK)
        {
            ++blacknum;
        }

        if (root->_col == RED && root->_parent->_col == RED)
        {
            cout << "存在连续的红色节点!!!" << endl;
            return false;
        }

        return _PrevCheck(root->_left, blacknum, benchmark)
            && _PrevCheck(root->_right, blacknum, benchmark);


    }


    // 功能函数
    //1、左单旋
    void RotateL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;

        parent->_right = subRL;

        if (subRL)
        {
            subRL->_parent = parent;
        }

        Node* ppNode = parent->_parent;

        subR->_left = parent;
        parent->_parent = subR;

        if (_root == parent)
        {
            _root = subR;
            subR->_parent = nullptr;
        }
        else
        {
            if (ppNode->_left == parent)
            {
                ppNode->_left = subR;
            }
            else
            {
                ppNode->_right = subR;
            }

            subR->_parent = ppNode;
        }
    }


    // 2、右单旋
    void RotateR(Node* parent)
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;

        parent->_left = subLR;

        if (subLR)
        {
            subLR->_parent = parent;
        }

        Node* ppNode = parent->_parent;

        subL->_right = parent;
        parent->_parent = subL;

        if (parent == _root)
        {
            _root = subL;
            subL->_parent = nullptr;
        }
        else
        {
            if (ppNode->_left == parent)
            {
                ppNode->_left = subL;
            }
            else
            {
                ppNode->_right = subL;
            }

            subL->_parent = ppNode;
        }
    }

    // 3、左右双旋
    void RotateLR(Node* parent)
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        int bf = subLR->_bf;

        RotateL(parent->_left);
        RotateR(parent);

    }

    // 4、右左双旋
    void RotateRL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;


        RotateR(parent->_right);
        RotateL(parent);
    }


private:
    Node* _root = nullptr;
};

test.cpp

#include <iostream>
#include <assert.h>
#include <time.h>
using namespace std;

#include "RedBlack.h"

void TestRBTree1()
{
    int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14, 0,5,30,25,20,4,13,30,28,27 };  // 测试双旋平衡因子调节
    //int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
    RBTree<int, int> t1;
    for (auto e : a)
    {
        t1.Insert(make_pair(e, e));
    }

    t1.InOrder();
    cout << "IsBalance:" << t1.IsBalance() << endl;
}

int main()
{
    TestRBTree1();
    return 0;
}

以上就是本文的全部内容,欢迎大家学习交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值