【数据结构】红黑树的实现

本篇博文主旨是介绍红黑树的概念及其性质,并用C++代码进行实现;红黑树的重难点是剖析插入、删除节点的旋转情况;最后再进行了红黑树和AVL树的对比,说明为什么红黑树优于AVL树

红黑树的概念及其性质

红黑树是一颗搜索二叉树,但不同之处是每个节点有颜色的标记;

除此之外,还有下列特点:

(1)每个节点的颜色为黑色或者红色,并且根节点为黑色

(2)从根节点到每个叶子节点的路径上,黑色节点的数量相同

(3)父亲节点和子节点不能同时为红色,也就是说,没有连续的红节点

(4)从根节点到叶子节点,最长路径不超过最短路径的两倍

红黑树节点的定义

//定义枚举类型变量
//表示节点颜色
enum Color
{
    RED,
    BLACK,
};

//红黑树节点的定义
template<typename K,typename V>
struct RBTreeNode
{
    //K/V 结构,可以存储一个pair
    K _key;
    V _value;

    //颜色
    Color _col;

    //节点我们采用三叉链,方便父亲的寻找
    RBTreeNode<K, V>* _left;
    RBTreeNode<K, V>* _right;
    RBTreeNode<K, V>* _parent;

    //构造函数
    RBTreeNode(const K& key,const V& value)
        : _key(key)
        , _value(value)
        , _left(NULL)
        , _right(NULL)
        , _parent(NULL)
        , _col(RED)
    {}
};

红黑树的插入以及旋转情况

这里,我们先定义一下几个节点的名称

parent—插入节点的父亲节点

cur — 当前插入的节点

grandfather — parent的父亲节点

uncle — 叔叔节点,grandfather除parent的另一个子节点

分以下情况讨论

情况1、叔叔存在且为红

当出现如图所示的这种情况时,parent和uncle,cur都为红;

这种情况只需要将parent,uncle的颜色换成黑色,grandfather节点设置为红色的

由于grandfather被设置成红色,那么需要向上调整,因为如果grandfather的父亲如果是红的,修改后就又出现了两个红色节点连续的情况了

《【数据结构】红黑树的实现》

情况2、叔叔不存在/叔叔存在,但是为黑色

这里又分两种情况:

a.插入的节点是parent的左孩子,那么直接对grandfather进行右旋,结束调整,因为最上面的节点为黑,不会导致出现两个连续红节点的情况

《【数据结构】红黑树的实现》

b.插入的节点是parent的右孩子,先需要对parent进行左旋,转化成a的情况,然后再对grandfather进行右旋,结束调整,因为最上面的节点为黑,不会导致出现两个连续红节点的情况

《【数据结构】红黑树的实现》

说明:上面列举的是parent为grandfather的左孩子的情况,反之,当parent是grandfather的右孩子时,也按照上面的方法

左旋和右旋的代码

//和AVL树的旋转逻辑相同,区别仅是不用修改平衡因子
    //右旋
    void RotateR(Node* parent)
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        Node* ppNode = parent->_parent;

        parent->_left = subLR;
        if (subLR)
            subLR->_parent = parent;

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

    //左旋
    void RotateL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        Node* ppNode = parent->_parent;

        parent->_right = subRL;
        if (subRL)
            subRL->_parent = parent; 

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

        if (ppNode)
        {
            if (ppNode->_left == parent)
                ppNode->_left = subR;
            else
                ppNode->_right = subR;

            subR->_parent = ppNode;
        }
        else
        {
            _root = subR;
            subR->_parent = NULL;
        }
    }

Insert函数的代码实现

    pair<Node*,bool> Insert(const K& key, const V& value)
    {
        //如果根节点为空,直接插入,并且将节点颜色设置为黑
        if (_root == NULL)
        {
            _root = new Node(key, value);
            _root->_col = BLACK;
            return make_pair(_root,true);
        }

        //用cur和parent 判断插入的Key知否存在
        //若不存在,用parent来表示要插入的节点的父亲
        Node* cur = _root;
        Node* parent = NULL;

        //进行循环查找
        while (cur)
        {
            if (key > cur->_key)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (key < cur->_key)
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                //不存在,返回查找到元素的指针,以及插入失败的FALSE
                return make_pair(cur, false);
            }
        }

        //进行插入
        cur = new Node(key, value);
        Node* newNode = cur;

        //判断插入的是parent的左孩子还是右孩子
        if (key < parent->_key)
        {
            parent->_left = cur;
            cur->_parent = parent;
        }
        else
        {
            parent->_right = cur;
            cur->_parent = parent;
        }

        //进行调整
        while (parent && parent->_col == RED)
        {
            Node* grandfather = parent->_parent;
            if (parent == grandfather->_left)
            {
                Node* uncle = grandfather->_right;
                //1.叔叔存在且为红,变色
                if (uncle && uncle->_col == RED)
                {
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;
                    cur = grandfather;
                    parent = cur->_parent;
                }
                else //叔叔不存在/叔叔存在且为黑
                {
                    //2.进行单旋
                    if (cur == parent->_left)
                    {
                        RotateR(grandfather);

                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else //3.进行双旋
                    {
                        RotateL(parent);
                        RotateR(grandfather);

                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    break;
                }
            }
            else
            {
                Node* uncle = grandfather->_left;
                //1.叔叔存在且为红,变色
                if (uncle && uncle->_col == RED)
                {
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;
                    cur = grandfather;
                    parent = cur->_parent;
                }
                else //叔叔不存在/叔叔存在且为黑
                {
                    //2.进行单旋
                    if (cur == parent->_right)
                    {
                        RotateL(grandfather);

                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else //3.进行双旋
                    {
                        RotateR(parent);
                        RotateL(grandfather);

                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    break;
                }
            }
        }

        _root->_col = BLACK;
        return make_pair(newNode, true);
    }

    bool IsBalance()
    {
        //1.空树,属于红黑树
        if (_root == NULL)
            return true; 

        //2.根节点不为黑
        if (_root->_col != BLACK)
            return false;

        //3.统计出一条路径上黑色节点的数量
        size_t k = 0;
        Node* cur = _root;
        while (cur)
        {
            if (cur->_col == BLACK)
                k++;

            cur = cur->_left;
        }

        //进行判别条件2和3的判断
        return CheckColor(_root) && CheckBlackNum(_root, k, 0);
    }

如何判断一棵树是否为红黑树

我们可以从下面三个方面判别,需要注意的是,空树也是红黑树

判别1:根节点是否为黑色

判别2:每条路径的黑色节点数量是否相同

判别3:每一个红节点,其父亲节点是否为黑节点

判别1:

    bool IsBalance()
    {
        //1.空树,属于红黑树
        if (_root == NULL)
            return true; 

        //2.根节点不为黑
        if (_root->_col != BLACK)
            return false;

        //3.统计出一条路径上黑色节点的数量
        size_t k = 0;
        Node* cur = _root;
        while (cur)
        {
            if (cur->_col == BLACK)
                k++;

            cur = cur->_left;
        }

        //进行判别条件2和3的判断
        return CheckColor(_root) && CheckBlackNum(_root, k, 0);
    }

判别2:

    bool CheckColor(Node* root)
    {
        //1.空树,满足
        if (root == NULL)
            return true;

        //2.判断父亲,是否是连续的红色
        if (root->_col == RED && root->_parent->_col == RED)
            return false;

        return CheckColor(root->_left) && CheckColor(root->_right);
    }

判别3:

    bool CheckBlackNum(Node* root, const size_t &k, size_t num)
    {
        //1.空树,满足
        if (root == NULL)
            return true;

        if (root->_col == BLACK)
            ++num;

        //2.叶子节点,判断黑色节点数量
        if (root->_left == NULL && root->_right == NULL && num != k)
            return false;

        return CheckBlackNum(root->_left, k, num) 
            && CheckBlackNum(root->_right, k, num);
    }

红黑树和AVL树的比较

1、红黑树相对AVL树来说,它不追求完全平衡,在他们的增删查改的时间复杂度都为O(lg(N))的情况下,降低了插入节点后的旋转次数;

2、红黑树没有AVL树的平衡因子的判断,实现起来简单了许多

3、而AVL树由于本身追求完全平衡的苛刻性,旋转次数多,旋转因子的判断较为麻烦

红黑树代码实现

下面是实现红黑树的github链接,可以关注一下,一起学习~
https://github.com/haohaosong/DataStruct/blob/master/RBTree.h

    原文作者:算法小白
    原文地址: https://blog.csdn.net/qq_31828515/article/details/56496533
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞