红黑树笔记分享

红黑树的概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或

Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路

径会比其他路径长出俩倍,因而是接近平衡的。

红黑树的性质

  1. 每个节点不是黑色就是红色
  2. 根是黑色的
  3. 如果一个节点是红色,那他的两个孩子一定是黑色的。
  4. 每条路径都有相同数目的黑色节点。
  5. 数路径要加上空节点,红黑树中的空节点(NIL节点)都是黑色的。

红黑树的模拟实现

1.节点

enum Colour
{
    Red,
    Black
};
template<class K, class V>
class RBNode
{
public:
    RBNode<K, V>* _left;
    RBNode<K, V>* _right;
    RBNode<K, V>* _parent;
    pair<K, V> _kv;
    Colour _col;
    RBNode(const pair<K, V>& kv)
        :_left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
        , _kv(kv)
        ,_col(Red)
    {}
};

和AVL不同的是:少了平衡因子,多了颜色。

2.插入

  1. 前面都和AVL或者说和二叉搜索树一样,先判断大小。
  2. 后面就要看插入后有没有违反红黑树的规则/性质。

因为红黑树的第四条性质,所以插入节点主要看uncle。

1.u为红

cur为新增节点,新增后有了连续的红节点,违反了第三条规则,而uncle为红色,因此需要进行调整:

把p和u变为黑,再把g变为红,

  1. 如果g为根,则调整完,把它再变回黑。
  2. 如果g不为根,则调整完,把g当成cur继续往上调整。

2.u不存在/u为黑

u的情况有两种:

  1. 如果u不存在,那么cur一定为新增节点,因为插入前,此树默认已经是红黑树,所以cur的p一定是红色

  1. 如果u存在,那么cur一定不为新增节点,原因同理,而此时的cur是红色,是因为子树因为情况1调整过来的。

解决方法:

  1. p为g的左孩子,cur为p的左孩子,则进行右单旋转
  2. p为g的右孩子,cur为p的右孩子,则进行左单旋转
  3. p、g变色--p变黑,g变红

3.u不存在/u为黑

并不是只有一边高,而是折线。

解决方法:

  1. p为g的左孩子,cur为p的右孩子,则针对p进行左右双旋
  2. p为g的右孩子,cur为p的左孩子,则针对p进行右左双旋
  3. cur、g变色--cur变黑,g变红

看图可以知道双旋的本质: cur的左右分给p和g,再让p和g做cur的左右。

//下面因为情况一和位置无关但二有关,所以写到一起也没啥事
while (parent && parent->_col == Red)
{
    Node* grandfather = parent->_parent;
    //情况一
    if (parent == grandfather->_left)
    {
        Node* uncle = grandfather->_right;
        if (uncle && uncle->_col == Red)
        {
            uncle->_col = parent->_col = Black;
            grandfather->_col = Red;
            //向上再处理
            cur = grandfather;
            parent = cur->_parent; // 此时如果parent不存在,就证明g就是根,while就不会进去,在下面会把根改成黑色。
        }
        //情况二
        else
        {
            //情况二,单旋
            if (cur == parent->_left)
            {
                RotateR(grandfather);
                parent->_col = Black;
                grandfather->_col = Red;
            }
            //情况三,双旋
            else if (cur == parent->_right)
            {
                RotateL(parent);
                RotateR(grandfather);
                cur->_col = Black;
                grandfather->_col = Red;
            }
            break;

        }

    }
    else
    {
        Node* uncle = grandfather->_left;
        if (uncle && uncle->_col == Red)
        {
            uncle->_col = parent->_col = Black;
            grandfather->_col = Red;
            //向上再处理
            cur = grandfather;
            parent = cur->_parent; // 此时如果parent不存在,就证明g就是根,while就不会进去,在下面会把根改成黑色。
        }
        else
        {
            //情况二,单旋
            if (cur == parent->_right)
            {
                RotateL(grandfather);
                parent->_col = Black;
                grandfather->_col = Red;
            }
            //情况三,双旋
            else if (cur == parent->_left)
            {
                RotateR(parent);
                RotateL(grandfather);
                cur->_col = Black;
                grandfather->_col = Red;
            }
            break;
        }
    }
}
_root->_col = Black;
return true;

3.检验

思路:

  1. 根节点不能为红色
  2. 不能有连续的红色节点
  3. 每条路径黑色节点的数量要相同

前两天很容易判断,第三条:

先记录一条路径(最左)的黑色节点数目作为标准,再递归走左子树右子树时,记录一下黑节点的数量,等递归到某一条路径为空时,黑节点记录完和标准值比较,如果不一样,就不是红黑树。

bool Check(Node* cur,int refBlacknum,int Blacknum)
    {
        if (cur == nullptr)
        {
            if (refBlacknum != Blacknum)
            {
                return false;
            }
            return true;
        }
        else if (cur && cur->_col == Red)//根为红
        {
            return false;
        }
        if (cur->_col == Black)
        {
            Blacknum++;
        }
        return Check(cur->_left,refBlacknum,Blacknum) && Check(cur->_right, refBlacknum, Blacknum);
    }
    bool IsBalance()
    {
        //连续的红节点
        if (_root == Red && _root->_parent == Red)
        {
            return false;
        }
        int refBlacknum = 0;
        Node* subL = _root;
        while (subL && subL->_left)
        {
            if(subL->_col == Black)
                refBlacknum++;
            subL = subL->_left;
        }
        
        return Check(_root,refBlacknum,0);
    }

【有道云笔记】红黑树
https://note.youdao.com/s/MLWAvCau

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值