C++红黑树(类模板实现)

简介: 红黑树(Red Black Tree)是一种特殊的二叉查找树(Binary Search Tree),满则如下红黑性质的二叉树是红黑树:1.每个节点或是红的,或是黑的2.根节点是黑的3.每个叶节点(NIL)是黑的4.如果一个节点是红的,则它的两个儿子都是黑的5.对于每个节点,从该节点到其子孙节点的所有路径上包含相同数目的黑节点。

红黑树(Red Black Tree)是一种特殊的二叉查找树(Binary Search Tree),满则如下红黑性质的二叉树是红黑树:
1.每个节点或是红的,或是黑的
2.根节点是黑的
3.每个叶节点(NIL)是黑的
4.如果一个节点是红的,则它的两个儿子都是黑的
5.对于每个节点,从该节点到其子孙节点的所有路径上包含相同数目的黑节点。
由于以上的性质,红黑树的效率能保证在log级别,而不会像普通的BST一样退化为线性的O(n)。

从红黑树的定义可以看出:
1.红黑树是一棵BST,满足BST的所有性质:左子树所有节点的值都不超过根节点,右子树所有节点的值都不小于根节点;中序遍历是升序的;...
2.若某节点是红色的,则必有黑父(黑色的父节点,下文中“红父”“黑叔”“红叔”等类推),且其两个子节点必为黑色(包括子节点为哨兵NIL的情况)
3.不存在两个连续的红色节点相连接,但连续两个黑节点可以存在;
...

在Linux中,不少地方使用了红黑树(C语言实现),在sourceforge等网站上可以参考其代码。


用C++实现红黑树,网上不少代码都参考了《算法导论》一书,其insert操作的讲解都正确无误,但删除操作总是含混不清,我认为原因在于不少讲解中都忽略了以下几点:
1.叶子节点(NIL)是哨兵,不是NULL,两者不应混淆
2.叶节点不应该被忽略(例如“人”子型的树,不是红黑树)

ok,明确以上两点过后,实现一棵红黑树应该不会太难了,下面贴出从罗索实验室找到的代码,链接的地址是http://www.rosoo.net/a/201207/16151.html

#pragma once
#ifdef MY_DEBUG
#include
#include "assert.h"
#endif //MY_DEBUG
namespace ScanbuyLib{
    enum rg_color  { black, red } ;
    enum e_balance { left_higher, equal_height, right_higher };
    enum e_return  { e_success, e_fail, e_empty, e_duplicate, e_not_found };
    enum e_order   { e_preorder, e_inorder, e_postorder };
    template class RBTreeNode
    {
    public:
        RBTreeNode(rg_color color = black);
        RBTreeNode(const K& key, const V& value, rg_color color= black);
    public:
        RBTreeNode* m_pRChild;
        RBTreeNode* m_pLChild;
        RBTreeNode* m_pParent;
        K key;
        V value;
        rg_color color;
    };
    template RBTreeNode::RBTreeNode(rg_color color)
    {
        m_pRChild = NULL;
        m_pLChild = NULL;
        m_pParent = NULL;
//        key = K(0);
//        value = V(0);
        this->color = color;
    }
    templateRBTreeNode::RBTreeNode(const K& key, const V& value, rg_color color)
    {
        m_pRChild = NULL;
        m_pLChild = NULL;
        m_pParent = NULL;
        this->key = key;
        this->value = value;
        this->color = color;
    }
    template class RedBlackTree
    {
    public:
        RedBlackTree();
        ~RedBlackTree();
        e_return insert(const K& key, const V& value);
        e_return remove(const K& key);
        e_return search(const K& key, V& value); // value as output
    private:
       
        void destroy(RBTreeNode* pNode);
        // make copy constructor and = operator private currently.
        RedBlackTree(const RedBlackTree&);
        RedBlackTree& operator = (const RedBlackTree& other);
       
        RBTreeNode* getGrandParent(RBTreeNode* pNode);
        RBTreeNode* getUncle(RBTreeNode* pNode);   
        RBTreeNode* getSibling(RBTreeNode* pNode);
#ifdef MY_DEBUG
        bool checkCorrectNess();
#endif //MY_DEBUG
        void insertFixup(RBTreeNode* pNode);
        void removeFixup(RBTreeNode* pNode);
        void rotateLeft (RBTreeNode* pNode);
        void rotateRight(RBTreeNode* pNode);
        RBTreeNode* m_pRoot;
        RBTreeNode* m_pSentinel;
    };

    template RedBlackTree::RedBlackTree()
    {
        // first instantiate the sentinel node, then make it root as sentinel
        m_pSentinel = new RBTreeNode();
        m_pSentinel->m_pLChild = NULL;
        m_pSentinel->m_pRChild = NULL;
        m_pSentinel->m_pParent = NULL;
        m_pSentinel->color = black;
        m_pRoot = m_pSentinel;
    }
    template RedBlackTree::~RedBlackTree()
    {
        // TODO, need to add it once really use it!!!!
        destroy(m_pRoot);
        if (m_pSentinel)
        {
            delete m_pSentinel;
            m_pSentinel = NULL;
        }
    }
   
    template   void RedBlackTree::destroy(RBTreeNode* pNode)
    {
        if (pNode != NULL && pNode != m_pSentinel)
        {
            destroy(pNode->m_pLChild);
            destroy(pNode->m_pRChild);   
            delete pNode;
            pNode = NULL;
        }
    }
    template   RBTreeNode* RedBlackTree::getGrandParent(RBTreeNode* pNode)
    {
        if (pNode && pNode->m_pParent)
            return pNode->m_pParent->m_pParent;
        else
            return NULL;
    }
    template RBTreeNode* RedBlackTree::getUncle(RBTreeNode* pNode)
    {
        RBTreeNode* pTemp = getGrandParent(pNode);
        if (pTemp == NULL)
            return NULL; // No grandparent means no uncle
        if (pNode->m_pParent == pTemp->m_pLChild)
            return pTemp->m_pRChild;
        else
            return pTemp->m_pLChild;
    }
    template RBTreeNode* RedBlackTree::getSibling(RBTreeNode* pNode)
    {
        if (pNode == NULL || pNode->m_pParent == NULL) return NULL;
        if (pNode == pNode->m_pParent->m_pLChild)
            return pNode->m_pParent->m_pRChild;
        else
            return pNode->m_pParent->m_pLChild;
    }

    template void RedBlackTree::rotateLeft(RBTreeNode* pNode)
    {
        if (pNode == NULL || pNode->m_pRChild == NULL)
            return;
        else
        {
            RBTreeNode* pTemp = pNode->m_pRChild;
            pNode->m_pRChild = pTemp->m_pLChild;
            if (pTemp->m_pLChild)
                pTemp->m_pLChild->m_pParent = pNode;
            if (pNode == m_pRoot)
            {
                m_pRoot = pTemp;
                pTemp->m_pParent = NULL;
            }
            else
            {
                pTemp->m_pParent= pNode->m_pParent;      
                if (pNode == pNode->m_pParent->m_pLChild)
                {
                    pNode->m_pParent->m_pLChild = pTemp;
                }
                else
                {
                    pNode->m_pParent->m_pRChild = pTemp;
                }
            }
            pTemp->m_pLChild = pNode;
            pNode->m_pParent = pTemp;
        }
    }
    template void RedBlackTree::rotateRight(RBTreeNode* pNode)
    {
        if (pNode == NULL || pNode->m_pLChild == NULL)
            return;
        else
        {
            RBTreeNode* pTemp = pNode->m_pLChild;
            pNode->m_pLChild = pTemp->m_pRChild;
            if (pTemp->m_pRChild)
                pTemp->m_pRChild->m_pParent = pNode;
            if (pNode == m_pRoot)
            {
                m_pRoot = pTemp;
                pTemp->m_pParent = NULL;
            }
            else
            {
                //update the parent
                pTemp->m_pParent= pNode->m_pParent;      
                if (pNode == pNode->m_pParent->m_pLChild)
                {
                    pNode->m_pParent->m_pLChild = pTemp;
                }
                else
                {
                    pNode->m_pParent->m_pRChild = pTemp;
                }            
            }
            pTemp->m_pRChild = pNode;
            pNode->m_pParent = pTemp;
        }
    }
    template e_return RedBlackTree::insert(const K& key, const V& value)
    {
        RBTreeNode* pTemp = m_pRoot;
        RBTreeNode* pParent = NULL;
        // init the new node here
        RBTreeNode* pNew = new RBTreeNode(key, value);
        pNew->color = red;
        pNew->m_pLChild = m_pSentinel;
        pNew->m_pRChild = m_pSentinel;
        // find the insert point
        while (pTemp != m_pSentinel)
        {
            pParent = pTemp;
            if (pTemp->key == key)
            {
                delete pNew;
                return e_duplicate;
            }
            pTemp = pTemp->key > key ? pTemp->m_pLChild: pTemp->m_pRChild;       
        }
        if (m_pRoot == m_pSentinel)
        {
            m_pRoot = pNew;
            m_pRoot->m_pParent = NULL;
        }
        else
        {
            pNew->m_pParent = pParent;
            if ( pParent->key > key )
            {
                pParent->m_pLChild= pNew;
            }
            else
            {
                pParent->m_pRChild= pNew;
            }   
        }
        insertFixup(pNew);
        //        insertCase1(pNew);
#ifdef MY_DEBUG       
        assert(checkCorrectNess());
#endif//MY_DEBUG
        return e_success;
    }
    template void RedBlackTree::insertFixup(RBTreeNode* pNode)
    {
        if (pNode == NULL) return; // impossible actually.
        RBTreeNode* pUncle = m_pSentinel;
        RBTreeNode* pGrandParent = NULL;
        while (pNode != m_pRoot && red == pNode->m_pParent->color)
        {
            pUncle = getUncle(pNode);
            pGrandParent = getGrandParent(pNode);
            if (pUncle != m_pSentinel && pUncle->color == red)
            {
                pNode->m_pParent->color = black;
                pUncle->color = black;
                pGrandParent->color = red;
                pNode = pGrandParent;
            }
            else
            {
                if (pNode->m_pParent == pGrandParent->m_pLChild)   
                {
                    if (pNode == pNode->m_pParent->m_pRChild)
                    {
                        pNode = pNode->m_pParent;
                        rotateLeft(pNode);
                    }
                    pNode->m_pParent->color = black;
                    pGrandParent->color = red;
                    rotateRight(pGrandParent);
                }
                else
                {
                    if (pNode == pNode->m_pParent->m_pLChild)
                    {
                        pNode = pNode->m_pParent;
                        rotateRight(pNode);
                    }
                    pNode->m_pParent->color = black;
                    pGrandParent->color = red;
                    rotateLeft(pGrandParent);
                }
            }
        }
        m_pRoot->color = black;
    }
    template e_return RedBlackTree::remove(const K& key)
    {
        // currently we won't use the
        if (!m_pRoot) return e_empty;
        RBTreeNode* pd = m_pRoot; // pd means pointer to the node deleted (with the same data with param:data)
        while (pd != m_pSentinel)
        {
            if (pd->key > key)
                pd = pd->m_pLChild;
            else if (pd->key < key)
                pd = pd->m_pRChild;
            else
                break; // equal so we find it!!!
        }
        if (pd == m_pSentinel) //haven't find it
            return e_not_found;
        // delete is not the real node to delete, but find a sub to replace and remove the sub
        RBTreeNode* pSub = NULL; // pSub is the really node to be sub
        // we can either find the max left child or min right child to sub
        // let's choose max left child here
        if (pd->m_pLChild == m_pSentinel && pd->m_pRChild == m_pSentinel)
            pSub = pd;
        else if (pd->m_pLChild == m_pSentinel)
            pSub = pd->m_pRChild;
        else if (pd->m_pRChild == m_pSentinel)
            pSub = pd->m_pLChild;
        else
        {
            pSub = pd->m_pLChild;
            // let's find the max left child
            while (pSub->m_pRChild != m_pSentinel)
            {
                pSub = pSub->m_pRChild;
            }
        }
        // replace the pd data with pSub's
        if (pd != pSub)
        {
            pd->key = pSub->key;
            pd->value = pSub->value;
        }
        // then find the child of sub and replace with sub
        RBTreeNode* pSubChild = pSub->m_pRChild != m_pSentinel ? pSub->m_pRChild: pSub->m_pLChild;
        if (pSub->m_pParent)
        {
            if (pSub == pSub->m_pParent->m_pLChild)
                pSub->m_pParent->m_pLChild = pSubChild;
            else
                pSub->m_pParent->m_pRChild = pSubChild;
        }
        else
        {
            m_pRoot = pSubChild;
        }
        //this may change the sentinel's parent to not-null value, will change to NULL later
        pSubChild->m_pParent = pSub->m_pParent;
        if (pSub->color == black)
            removeFixup(pSubChild);
        if (pSub)
        {
            delete pSub;
            pSub = NULL;
        }
        // rollback sentinel's parent to NULL;
        m_pSentinel->m_pParent = NULL;
#ifdef MY_DEBUG
        assert(checkCorrectNess());
#endif //MY_DEBUG
        return e_success;
    }
    template void RedBlackTree::removeFixup(RBTreeNode* pNode)
    {
        RBTreeNode* pSibling = NULL;
        while ((pNode != m_pRoot) && (pNode->color == black))
        {
            pSibling = getSibling(pNode);
            if (pNode == pNode->m_pParent->m_pLChild) // left child node
            {
                if (pSibling->color == red)
                {
                    // case 1, can change to case 2, 3, 4
                    pNode->m_pParent->color = red;
                    pSibling->color = black;
                    rotateLeft(pNode->m_pParent);
                    // change to new sibling,
                    pSibling = pNode->m_pParent->m_pRChild;
                }
                // case 2;
                if ((black == pSibling->m_pLChild->color) && (black == pSibling->m_pRChild->color))
                {
                    pSibling->color = red;
                    pNode = pNode->m_pParent;
                }
                else
                {
                    if (black == pSibling->m_pRChild->color)
                    {
                        pSibling->color = red;
                        pSibling->m_pLChild->color = black;
                        rotateRight(pSibling);
                        pSibling = pNode->m_pParent->m_pRChild;
                    }
                    pSibling->color = pNode->m_pParent->color;
                    pNode->m_pParent->color = black;
                    pSibling->m_pRChild->color = black;
                    rotateLeft(pNode->m_pParent);
                    break;
                }
            }
            else
            {
                if (pSibling->color == red)
                {
                    // case 1, can change to case 2, 3, 4
                    pNode->m_pParent->color = red;
                    pSibling->color = black;
                    rotateRight(pNode->m_pParent);
                    // change to new sibling,
                    pSibling = pNode->m_pParent->m_pLChild;
                }
                // case 2;
                if ((black == pSibling->m_pLChild->color) && (black == pSibling->m_pRChild->color))
                {
                    pSibling->color = red;
                    pNode = pNode->m_pParent;
                }
                else
                {
                    if (black == pSibling->m_pLChild->color)
                    {
                        pSibling->color = red;
                        pSibling->m_pRChild->color = black;
                        rotateLeft(pSibling);
                        pSibling = pNode->m_pParent->m_pLChild;
                    }
                    pSibling->color = pNode->m_pParent->color;
                    pNode->m_pParent->color = black;
                    pSibling->m_pLChild->color = black;
                    rotateRight(pNode->m_pParent);
                    break;
                }
            }
        }
        pNode->color = black;
    }

    template e_return RedBlackTree::search(const K& key, V& value) // value as output
    {
        if (!m_pRoot) return e_empty;
       
        RBTreeNode* pTemp = m_pRoot;
        while (pTemp != m_pSentinel)
        {
            if (pTemp->key < key)
                pTemp = pTemp->m_pRChild;
            else if (pTemp->key > key)
                pTemp = pTemp->m_pLChild;
            else
                break;
        }
        if (pTemp != m_pSentinel)
        {
            //find it now!
            value = pTemp->value;
            return e_success;
        }
        else
        {
            return e_not_found;
        }
    }
#ifdef MY_DEBUG
    template bool RedBlackTree::checkCorrectNess()
    {
        if (!m_pRoot)
            return true;
        bool bRet = true;
        // check if the root color is black
        if (m_pRoot && m_pRoot->color == red)
            bRet = false;
        // check red node with black child
        std::queue< RBTreeNode* > oQueue;                                  
        oQueue.push( m_pRoot );
        int nCurLevelCount = 1;
        int length = -1;
        while (true)
        {
            int nNextLevelCount     = 0;      
            while (nCurLevelCount)
            {
                RBTreeNode* pNode = oQueue.front();
                nCurLevelCount -- ;
                if(pNode->color == red)
                {
                    // child color is black
                    if ((pNode->m_pLChild && pNode->m_pLChild->color == red) ||
                        (pNode->m_pRChild && pNode->m_pRChild->color == red))
                    {
                        bRet = false;
                        break;
                    }   
                }
                if ( !pNode->m_pLChild && !pNode->m_pRChild)
                {
                    // this is the leaf node, check the path root
                    int len = 0;
                    RBTreeNode* pTemp = pNode;
                    while (pTemp->m_pParent)
                    {
                        if (pTemp->color == black)
                            len ++ ;
                        pTemp = pTemp->m_pParent;
                    }
                    if (length == -1)
                        length = len;
                    else
                    {
                        if (len != length)
                        {
                            bRet = false;
                            break;
                        }
                    }
                }
                if (pNode->m_pLChild)
                {
                    oQueue.push( pNode->m_pLChild );
                    nNextLevelCount++;    
                }
                if (pNode->m_pRChild)
                {
                    oQueue.push( pNode->m_pRChild );
                    nNextLevelCount++;    
                }
                oQueue.pop();
            }
            if (!bRet)
                break;
            nCurLevelCount = nNextLevelCount;
            if (!nCurLevelCount)
                break;
        }
        return bRet;
    }
#endif //MY_DEBUG
}




当然,网上还是有很多其他人的博客可以参考的,这里简单列举:
http://blog.csdn.net/v_july_v/article/details/6105630  July的博客
http://saturnman.blog.163.com/    saturman的博客
http://www.cppblog.com/converse/archive/2012/11/27/66530.html#195744   那谁的博客
http://lxr.linux.no/#linux+v3.7.1/lib/rbtree.c           linux的rbtree代码

此外,读者可以参考
《算法导论》(建议看一下英文版的)
http://zh.wikipedia.org/wiki/红黑树
http://en.wikipedia.org/wiki/Red–black_tree

个人推荐维基百科英文的讲解,分case讲解很清晰,而且求节点的grandfather等的操作也很安全。

目录
相关文章
|
3天前
|
存储 编译器 C语言
c++的学习之路:5、类和对象(1)
c++的学习之路:5、类和对象(1)
19 0
|
17天前
|
存储 C++ 容器
C++STL(标准模板库)处理学习应用案例
【4月更文挑战第8天】使用C++ STL,通过`std:vector`存储整数数组 `{5, 3, 1, 4, 2}`,然后利用`std::sort`进行排序,输出排序后序列:`std:vector<int> numbers; numbers = {5, 3, 1, 4, 2}; std:sort(numbers.begin(), numbers.end()); for (int number : numbers) { std::cout << number << " "; }`
19 2
|
28天前
|
存储 C++ 容器
C++入门指南:string类文档详细解析(非常经典,建议收藏)
C++入门指南:string类文档详细解析(非常经典,建议收藏)
37 0
|
3天前
|
C++
c++的学习之路:7、类和对象(3)
c++的学习之路:7、类和对象(3)
19 0
|
2天前
|
设计模式 Java C++
【C++高阶(八)】单例模式&特殊类的设计
【C++高阶(八)】单例模式&特殊类的设计
|
2天前
|
编译器 C++
【C++基础(八)】类和对象(下)--初始化列表,友元,匿名对象
【C++基础(八)】类和对象(下)--初始化列表,友元,匿名对象
|
6天前
|
存储 安全 C语言
【C++】string类
【C++】string类
|
存储 编译器 Linux
标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”
标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”
|
8天前
|
编译器 C++
标准库中的string类(上)——“C++”
标准库中的string类(上)——“C++”
|
8天前
|
编译器 C++
自从学了C++之后,小雅兰就有对象了!!!(类与对象)(中)——“C++”
自从学了C++之后,小雅兰就有对象了!!!(类与对象)(中)——“C++”

热门文章

最新文章