码迷,mamicode.com
首页 > 其他好文 > 详细

红黑树

时间:2017-05-11 17:41:43      阅读:306      评论:0      收藏:0      [点我收藏+]

标签:log   comment   不能   nts   std   south   ring   line   ext   

  • 红黑树概念
    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);

    }

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

红黑树

标签:log   comment   不能   nts   std   south   ring   line   ext   

原文地址:http://blog.csdn.net/jyy305/article/details/71636992

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!