红黑树

来源:互联网 发布:虎鲸vs大白鲨 知乎 编辑:程序博客网 时间:2024/06/06 00:54
  • 红黑树概念
    1.红黑树首先是一个二叉查找树,他的每个节点都被标有颜色(红色或者黑色)红黑树满足以下五个性质

1.每个节点的颜色只能是黑色或者是红色
2.根节点是黑色的
3.如果一个节点的颜色是红色,则他的两个孩子都是黑的,也就是说一条路径上不能出现两个相邻的红色节点。
4.对每个节点来说 ,从该节点到其子孙叶子节点的所有路径上,黑色节点的个数相同。
5.每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

  • 红黑树的插入操作
    红黑树必须满足上述的5个性质,但是插入节点后可能会导致失衡,此时需要做一定的调整。下面总结了导致失衡的情况,以及相应的调整措施。

情形1:parent是grendParent的左孩子,并且uncle节点存在且为红色。

这里写图片描述

调整方式:将parent和uncle节点改为黑色,并将grendParent改为红色,然后继续向上调整。

            Node *grandParent = parent->_pParent;            if (parent == grandParent->_pLeft)            {                Node *uncle = grandParent->_pRight;                if (uncle && RED == uncle->_color)                {                    parent->_color = BLACK;                    uncle->_color = BLACK;                    grandParent->_color = RED;                    pNew = grandParent;                    parent = grandParent->_pParent;                }

情形2: parent是grendParent的左孩子,插入节点是parent的左孩子,并且uncle节点不存在或者为黑色。

这里写图片描述
这里写图片描述

调整方式:将parent与grandParent的颜色互换,并进行右旋。

情形3:parent是grendParent的左孩子,插入节点是parent的右孩子,并且uncle节点不存在或者为黑色。

这里写图片描述

调整方式:先进行左旋,再将parent与grandParent的颜色互换,并进行右旋。

else//uncle节点不存在或者为黑                {                    if (pNew == parent->_pRight)                    {                        lRotate(parent);                        std::swap(parent, pNew);                    }                    parent->_color = BLACK;                    grandParent->_color = RED;                    rRotate(grandParent);                }

情形4:parent是grendParent的右孩子,并且uncle节点存在且为红色。

这里写图片描述

调整方式:将parent和uncle节点改为黑色,并将grendParent改为红色,然后继续向上调整。

else//parent 为grandParent的右孩子            {                Node *uncle = grandParent->_pLeft;                if (uncle && RED == uncle->_color)                {                    parent->_color = BLACK;                    uncle->_color = BLACK;                    grandParent->_color = RED;                    pNew = grandParent;                    parent = grandParent->_pParent;                }

情形5: parent是grendParent的做右孩子,插入节点是parent的右孩子,并且uncle节点不存在或者为黑色。

这里写图片描述

调整方式:将parent与grandParent的颜色互换,并进行左旋。

情形6:parent是grendParent的右孩子,插入节点是parent的左孩子,并且uncle节点不存在或者为黑色。

这里写图片描述

调整方式:先进行右旋,再将parent与grandParent的颜色互换,并进行左旋。

综上其实可以分为parent是grendParent的左孩子或者右孩子两大类,两种情况又分别有三种情况,所以完整的插入代码如下

bool Insert(const K &key, const V &value)    {        Node *pNew = new Node(key, value);        if (NULL == _pRoot)        {            _pRoot = pNew;            _pRoot->_color = BLACK;            return true;        }        //寻找插入位置        Node *parent = NULL;        Node *pCur = _pRoot;        while (pCur)        {            if (key < pCur->_key)            {                parent = pCur;                pCur = pCur->_pLeft;            }            else if (key > pCur->_key)            {                parent = pCur;                pCur = pCur->_pRight;            }            else                return false;        }        //插入节点        if (key < parent->_key)            parent->_pLeft = pNew;        else            parent->_pRight = pNew;        pNew->_pParent = parent;        //判断是否违反性质进行调整        while (parent && parent != _pRoot && RED == parent->_color)        {            Node *grandParent = parent->_pParent;            if (parent == grandParent->_pLeft)            {                Node *uncle = grandParent->_pRight;                if (uncle && RED == uncle->_color)                {                    parent->_color = BLACK;                    uncle->_color = BLACK;                    grandParent->_color = RED;                    pNew = grandParent;                    parent = grandParent->_pParent;                }                else//uncle节点不存在或者为黑                {                    if (pNew == parent->_pRight)                    {                        lRotate(parent);                        std::swap(parent, pNew);                    }                    parent->_color = BLACK;                    grandParent->_color = RED;                    rRotate(grandParent);                }            }            else//parent 为grandParent的右孩子            {                Node *uncle = grandParent->_pLeft;                if (uncle && RED == uncle->_color)                {                    parent->_color = BLACK;                    uncle->_color = BLACK;                    grandParent->_color = RED;                    pNew = grandParent;                    parent = grandParent->_pParent;                }                else//uncle节点不存在或者为黑                {                    if (pNew == parent->_pLeft)                    {                        rRotate(parent);                        std::swap(parent, pNew);                    }                    parent->_color = BLACK;                    grandParent->_color = RED;                    lRotate(grandParent);                }            }        }        _pRoot->_color = BLACK;        return true;    }

左旋及右旋代码

void lRotate(Node *parent)    {        Node *subR = parent->_pRight;        Node *subRL = subR->_pLeft;        parent->_pRight = subRL;        if (subRL)        subRL->_pParent = parent;        subR->_pLeft = parent;        Node *pparent = parent->_pParent;        parent->_pParent = subR;        subR->_pParent = pparent;        if (NULL == pparent)        {            _pRoot = subR;        }        else        {            if (pparent->_pLeft == parent)                pparent->_pLeft = subR;            else                pparent->_pRight = subR;        }    }    void rRotate(Node *parent)    {        Node *subL = parent->_pLeft;        Node *subLR = subL->_pRight;        parent->_pLeft = subLR;        if (subLR)            subLR->_pParent = parent;        subL->_pRight = parent;        Node *pparent = parent->_pParent;        parent->_pParent = subL;        subL->_pParent = pparent;        if (NULL == pparent)        {            _pRoot = subL;        }        else        {            if (pparent->_pLeft == parent)                pparent->_pLeft = subL;            else                pparent->_pRight = subL;        }    }
  • 测试代码
    插入操作完成后为了验证我们的操作是否正确,我们可以自己写一个验证代码来检测一下。
bool Test()    {        if (NULL == _pRoot)            return true;        if (BLACK != _pRoot->_color)        {            cout << "不满足性质1:根的颜色为黑色" << endl;            return false;        }        size_t numofBlackN = 0;        Node *pCur = _pRoot;        while (pCur)        {            if (BLACK == pCur->_color)                numofBlackN++;            pCur = pCur->_pLeft;//统计最左边分支的黑色节点个数。        }        return _test(_pRoot, numofBlackN, 0);    }bool _test(Node *pRoot, size_t numofBlackN, size_t index)    {        if (NULL == pRoot)            return true;        if (RED == pRoot->_color)        {            if (pRoot->_pLeft && RED == pRoot->_pLeft->_color || pRoot->_pRight && RED == pRoot->_pRight->_color)            {                cout << "违反性质3:两个红色节点相邻" << endl;                return false;            }        }        if (BLACK == pRoot->_color)            index++;        if (NULL == pRoot->_pLeft && NULL == pRoot->_pRight)        {            if (numofBlackN != index)            {                cout << "违反性质4:每条路径黑色节点个数不相同" << endl;                return false;            }        }        return _test(pRoot->_pLeft, numofBlackN, index) && _test(pRoot->_pRight, numofBlackN, index);    }

上述代码只能测试是否满足红黑树的5个性质,红黑树也是一个二叉搜索树,我们要检验二叉搜索树的正确性,可以采用中序遍历,看看是否正确。

void inorder()    {        _inorder(_pRoot);    }void _inorder(Node *pRoot)    {        if (NULL == pRoot)            return;        _inorder(pRoot->_pLeft);        cout << pRoot->_key << "->";        _inorder(pRoot->_pRight);    }

自此我们的红黑树插入操作就完成了。

  • 完整代码请戳:
    https://coding.net/u/Hyacinth_Dy/p/MyCode/git/blob/master/%E7%BA%A2%E9%BB%91%E6%A0%91%E5%AE%8C%E6%95%B4%E7%89%88
1 0
原创粉丝点击