RBT红黑树

引入

最开始我们学习了搜索二叉树,但是最后我们发现搜索二叉树有缺陷,之后我们又引入了AVL树。AVL树是一种高度平衡的二叉搜索树,能够满足增删查都是O(logN)的时间复杂度。既然AVL树已经满足了我们的期望,那么为什么还要引入红黑树呢?
这是由于AVL树是高度平衡的二叉搜索树,维持一颗AVL树的代价相比于红黑树来说是非常大的,由于它的高度平衡,使得它几乎每次插入或者删除都需要调整树。而红黑树是一种近似平衡的二叉搜索树,它满足最长路径不超过最短路径的两倍,而且它易于维护,在插入的时候也不是每次都需要调整树。
虽然红黑树整体性能上较AVL树能差一点,但是也不是差的太多,而且红黑树胜在易于维护,所以折中下来红黑树还是用的比较多的。

概念

红黑树是一颗二叉搜索树,他在没一个节点上增加了一个存储位来表示节点的颜色,可以是红色或者黑色,通过任何一条从根到叶子简单路径上的颜色来约束,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡。
性质:
(1)每个节点不是红色就是黑色的
(2)根节点是黑色的
(3)如果一个节点是红色的,则它的两个孩子是黑色的
(4)对于每个节点,从该节点到其所有后代节点的简单路径上,均包含相同数目的黑色节点
(5)每个叶子节点都是黑色的(此处的叶子节点指的是空节点)

插入操作

一般情况下,我么将要插入的结点都默认设置成红色的。
(1)如果插入的结点是根节点,则直接插入,并且将根节点染成黑色。
(2)如果要插入的位置的父亲是黑色的,那么直接插入。
(3)要插入的位置的父亲是红色的,这时再插入一个红色结点就会出现两个红色结点连续的情况,所以这时候我们就要调整树来重新恢复平衡。假设要插入的结点是cur,它的父亲是parent,它父亲的兄弟是uncle,它的祖父是grand。
注:(下面只演示parent为grandfather左孩子的情况)
情景一:父节点parent为红,叔叔节点uncle存在且为红。
这里写图片描述
情景二:父节点parent为红,叔叔节点uncle存在且为黑。
(1)插入节点是parent的左孩子。
这里写图片描述
(2)插入节点是parent的右孩子。
这里写图片描述
代码实现:

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);
    }

    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
        {
            return make_pair(cur, false);
        }
    }

    //判断将节点插入父节点的左边还是右边 
    cur = new Node(key, value);
    Node* newNode = cur;
    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.cur在父节点左边进行单旋
                if (cur == parent->_left)
                {
                    RotateR(grandfather);

                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                //3.cur在父节点右边进行双旋
                else if(cur == parent->_right)
                {
                    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;
                }
                 //3.进行双旋
                else
                {
                    RotateR(parent);
                    RotateL(grandfather);

                    cur->_col = BLACK;
                    grandfather->_col = RED;
                }
                break;
            }
        }
    }
    _root->_col = BLACK;//将根节点置为黑色
    return make_pair(newNode, true);
}

判断是否为红黑树

由红黑树的性质可以总结如下判别条件:
(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;
    }
    return CheckColor(_root) && CheckBlackNum(_root, k, 0);
}

(2)每条路径上的黑色节点数是否相同

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

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

//叶子节点,判断黑色节点数量
if (root->_left == NULL && root->_right == NULL && num != k)
{
    cout<<"路径上的黑色节点数不相同"<<endl;
    return false;
}

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

(3)每一个红节点,其父亲节点是否为黑节点

bool CheckColor(Node* root)
{
    //1.空树,满足
    if (root == NULL)
    {
        return true;
    }
    //2.判断父亲,是否是连续的红色
    if (root->_col == RED && root->_parent->_col == RED)
    {
        cout<<"存在连续的红节点"<<endl;
        return false;
    }
    return CheckColor(root->_left) && CheckColor(root->_right);
}

RBT与AVL

(1)红黑树相对AVL树来说,它不追求完全平衡,在他们的增删查改的时间复杂度都为O(lg(N))的情况下,降低了插入节点后的旋转次数;
(2)红黑树没有AVL树的平衡因子的判断,实现起来简单了许多
(3)而AVL树由于本身追求完全平衡的苛刻性,旋转次数多,旋转因子的判断较为麻烦

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值