RBtree

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!-【码炫课堂收费课节选之-红黑树源码解析及手写红黑树】_哔哩哔哩_bilibili

B站的听课记录,并写下如下红黑树c++版本代码,该课程真的史诗级推荐!

/*RBtreeNode.h*****/
#pragma once
enum { RED = false, BLACK = true };
template<class K,class V >
class RBtreeNode
{
public:
	// 红黑树的左右节点及父节点
	RBtreeNode<K, V>* parent;
	RBtreeNode<K, V>* left;
	RBtreeNode<K, V>* right;
	bool color;
	// 创建key 和 value的值
	K key;
	V value;
	/*构造函数**/
	RBtreeNode();
	RBtreeNode(RBtreeNode<K,V>* parent, RBtreeNode<K, V>* left, RBtreeNode<K, V>* right, K key, V value,bool color);
	RBtreeNode(K key,V value, RBtreeNode<K, V>* parent);
	void setVlue(K key,V value);
};





/*RBtreeNode.cpp*****/
#include "RBtreeNode.h"
// 默认构造函数,里面没有内容
template<class K,class V>
RBtreeNode<K, V>::RBtreeNode()
{
	this->parent = nullptr;
	this->left = nullptr;
	this->right = nullptr;
	// 主要是根节点为黑色,因此默认构造函数为黑色
	this->color = BLACK; 
}
template<class K, class V>
RBtreeNode<K, V>::RBtreeNode(RBtreeNode<K, V>* parent, RBtreeNode<K, V>* left, RBtreeNode<K, V>* right, K key, V value,bool color)
{
	this->parent = parent;
	this->left = left;
	this->right = right;
	this->key = key;
	this->value = value;
	this->color = color;
}
template<class K, class V>
RBtreeNode<K, V>::RBtreeNode(K key, V value, RBtreeNode<K, V>* parent)
{
	this->key = key;
	this->value = value;
	this->parent = parent;
	this->color = BLACK;
}
template<class K, class V>
void RBtreeNode<K, V>::setVlue(K key, V value)
{
	this->key = key;
	this->value = value;
}







/*RBtree.h*****/
#pragma once
#include "RBtreeNode.h"
using namespace std;
template<class K,class V>
class RBtree
{
private:
	RBtreeNode<K,V>* root;
	RBtreeNode<K, V>* rightof(RBtreeNode<K, V>* p) { return p->right; }; //获取右子
	RBtreeNode<K, V>* leftof(RBtreeNode<K, V>* p) { return p->left; }; //获取左子
	RBtreeNode<K, V>* parentof(RBtreeNode<K, V>* p) { return p->parent; }//获取父亲
	// 默认空节点
	RBtreeNode<K, V>* colorof(RBtreeNode<K, V>* p) { return p == nullptr ? p->color = BLACK : p->color = RED; } // 颜色更改
	const RBtreeNode<K, V>* getRoot() { return root; }
	void setRoot(RBtreeNode<K, V>* temp) { root = temp; }
	void fixAfterput(RBtreeNode<K, V>* p);
	void fixAfterdelete(RBtreeNode<K, V>* p); // 删除之后再修改
	// 查找前继节点
	RBtreeNode<K, V>* predecessor(RBtreeNode<K, V>* p);
	RBtreeNode<K, V>* successor(RBtreeNode<K, V>* p);

public:
	/*公有接口**/
	void leftRotate(RBtreeNode<K, V>* p);
	void rightRotate(RBtreeNode<K, V>* p);
	void put(K key, V value);
	// 删除节点操作
	void deleteNode(RBtreeNode<K, V>* p);
	
};














/*RBtree.cpp*******/
#include "RBtree.h"
/**
     * 围绕p左旋
     *       pf                    pf
     *      /                     /
     *     p                     pr(r)
     *    / \          ==>      / \
     *  pl  pr(r)              p   rr
     *     / \                / \
     *    rl  rr             pl  rl
     *
     * @param p
     */
template<class K,class V>
void RBtree<K, V>::leftRotate(RBtreeNode<K, V>* p)
{
    if (p != nullptr)
    {
        RBtreeNode<K, V>* r = p->right;
        p->right = r->left;
        if (r->left != nullptr)
        {
            r->left->parent = p;
        }
        r->parent = p->parent;
        if (p->parent == nullptr)
        {
            r = root;
        }
        else if (p->parent->left == p)
        {
            p->parent->left = r;
        }
        else if (p->parent->right == p)
        {
            p->parent->right = r;
        }
        r->left = p;
        p->parent = r;
    }
}
/**
     * 右旋
     *    pf                pf
     *     \                 \
     *      p             (l)pl
     *     / \      =>      /  \
     *(l)pl  pr            ll   p
     *   / \                   / \
     *  ll lr                 lr  pr
     *
     * @param p
     */
template<class K, class V>
void RBtree<K, V>::rightRotate(RBtreeNode<K, V>* p)
{
	if (p != nullptr)
	{
		RBtreeNode<K, V>* l = p->left;
		p->left = l->right;
		if (l->right != nullptr)
		{
			l->right->parent = p;
		}
		l->parent = p->parent;
		if (p->parent == nullptr)
		{
			l = root;
		}
		else if (p->parent->left == p)
		{
			p->parent->left = l;
		}
		else if (p->parent->right == p)
		{
			p->parent->right = l;
		}
		l->right = p;
		p->parent = l;
	}
}
// c++没有类型,貌似不能设置空值
template<class K, class V>
void RBtree<K, V>::put(K key, V value)
{
    // 申明一个临时变量指向跟节点,通过根节点进行左右寻找
    RBtreeNode<K, V>* p = root;
    RBtreeNode<K, V>* dad; // 来一个临时变量指向父亲
    int cmp; // 比较常量
    // 1、第一种情况若没有根节点,则此插入元素便为根节点
    if (p == nullptr)
    {
        root = new RBtreeNode<K, V>(key, value , nullptr);
        return;
    }
    // 2、第二种情况不是根节点则先根据循环查找要插入节点的位置
    do 
    {   // 直接通过比较大小的符号来比较key
        dad = p; 
        if (key > p->key) // 更大向右边找
        {
            p = p->right; //向右边找
            cmp = 1; // 更大为1
        }
        else if (key < p->key)//更小向左边找
        {
            p = p->left;//向左边找
            cmp = -1; // 更小为-1
        }
        else if (key == p->key)//若找到相等的直接替代
        {
            p->setVlue(key,value);//替代
            return;
        }
    } while (p != nullptr); // r一个个向下寻找,到最后寻找到空指针之后便退出循环
    RBtreeNode<K, V>* e = new RBtreeNode<K, V>(key, value, dad);
    // 找到之后便开始赋值
    if (cmp == 1)
    {
        dad->right = e;
    } 
    else if(cmp == -1)
    {
        dad->left = e;
    }
    // 调整函数
    fixAfterput(e);
}
template<class K, class V>
void RBtree<K, V>::fixAfterput(RBtreeNode<K, V>* p)
{
    p->color = RED; // 新加进来的元素是红色
    while (p != nullptr && p != root && p->parent->color == RED)
    {
        // 判断左三右三的情况
        // 用爷爷和爸爸查找
        if (parentof(parentof(p))->left == parentof(p))
        {
            // 满足情况是左三
            /*
             * 左3分为两种情况
             * 1、四节点
             * 2、三节点
             */
            RBtreeNode<K, V>* u = parentof(parentof(p))->right; // 令u为叔叔节点
            if ( u->color == RED) // 叔叔节点为红色说明叔叔的存在,若为空节点系统将默认给黑
            {
                // 叔叔节点存在则进入四节点情况
                //       p
                //      / \
                //     a   e
                //          \
                //           x(新节点)
                // 直接变色
                parentof(parentof(p))->color = RED;
                u->color = BLACK;
                parentof(p)->color = BLACK;
                // 此时爷爷变红,因此要考虑爷爷和太爷爷之间的关系
                // 因为这里直接变红了
                p = parentof(parentof(p));
            }
            else // 若叔叔节点不存在则是三节点添加新节点的情况
            {
                // 叔叔节点添加新节点分为常规和特殊情况
                // 特殊情况就是 e              e
                //             /              /
                //            j              j
                //             \            /
                //               p         p
                // 需要先旋转处理成正常情况
                if (p->parent->right == p)
                {
                    p = p->parent;
                    leftRotate(p);
                    // 处理完以上步骤之后便成为了常规的左偏
                }
                // 需要旋转变色
				parentof(parentof(p))->color = RED;
				parentof(p)->color = BLACK;
				p->color = RED;
                // 此时注意应该要先变色再旋转
                // 因为旋转之后父类就变了
                rightRotate(parentof(parentof(p)));
                // 旋转之后 j
                //         / \
                //        p   e 
            }

        }
        else
        {
            // 否则就是右三的
            /*
             * 右3分为两种情况
             * 1、四节点
             * 2、三节点
             */
            RBtreeNode<K, V>* u = parentof(parentof(p))->left; // 令u为叔叔节点
            if (u->color == RED) // 不为空节点说明叔叔节点存在
            {
                // 叔叔节点存在则进入四节点情况
                // 直接变色
                parentof(parentof(p))->color = RED;
                u->color = BLACK;
                parentof(p)->color = BLACK;
                // 此时爷爷变红,因此要考虑爷爷和太爷爷之间的关系
				// 因为这里直接变红了
				p = parentof(parentof(p));
            }
            else // 若叔叔节点不存在则是三节点添加新节点的情况
            {
                // 叔叔节点添加新节点分为常规和特殊情况
                // 特殊情况就是 e              e
                //              \              \
                //               j              j
                //                \            /
                //                 p          p
                // 需要先旋转处理成正常情况
                if (p->parent->left == p)
                {
                    p = p->parent;
                    rightRotate(p);
                    // 处理完以上步骤之后便成为了常规的左偏
                }
                // 需要旋转变色
                parentof(parentof(p))->color = RED;
                parentof(p)->color = BLACK;
                p->color = RED;
                // 此时注意应该要先变色再旋转
                // 因为旋转之后父类就变了
                leftRotate(parentof(parentof(p)));
                // 旋转之后 j
                //         / \
                //        p   e 
            }
        }
    }
    root->color = BLACK;
}
// 前继节点-小于该节点的最大值
template <class K,class V>
RBtreeNode<K, V>* RBtree<K, V>::predecessor(RBtreeNode<K, V>* p)
{
    
    if (p == nullptr)
    {
        return nullptr;
    }
    else if(p->left != nullptr) // 若左子不为0,先去左子
	{
        RBtreeNode<K, V>* node;
        node = p->left;
		//第一种情况 p
	    //          / 
	    //         a  
	    //          \
        //           x
	    //也是不拐弯的情况
        //因此找到左子之后无限向右循环
        //来找到小于p值的最大值
        while (node->right != nullptr)
        {
            node = node->right;
        }
        return node;
    }
    else
    {
        //没有左子,就是特殊情况
        //   a
        //  / \
        // x    b
        //    /
        //   c
        //  /
        // p
        RBtreeNode<K, V>* parentT = p->parent; // 创建指向父节点的临时变量
        RBtreeNode<K, V>* node = p; // 创建临时变量node
        while (parentT->left == node && parentT != nullptr) //一直到拐弯或者到了跟节点
        {
            node = parentT;
            parentT = parentof(parentT);
        }
        return parentT;
    }
}

// 后续节点-大于该节点的最小值
template <class K, class V>
RBtreeNode<K, V>* RBtree<K, V>::successor(RBtreeNode<K, V>* p)
{
	if (p == nullptr)
	{
		return nullptr;
	}
	else if (p->right != nullptr) 
	{
		RBtreeNode<K, V>* node;
		node = p->right;
		while (node->left != nullptr)
		{
			node = node->left;
		}
		return node;
	}
	else
	{
		RBtreeNode<K, V>* parentT = p->parent; // 创建指向父节点的临时变量
		RBtreeNode<K, V>* node = p; // 创建临时变量node
		while (parentT->right == node && parentT != nullptr) //一直到拐弯或者到了跟节点
		{
			node = parentT;
			parentT = parentof(parentT);
		}
		return parentT;
	}
}
/*删除操作:
 *1、删除叶子节点直接删除
 *2、被删除的节点有一个子节点,删除它之后使用子节点替代它
 *3、被删除的节点有两个子节点,查找前继或后续节点来替代被删除节点
 */
template <class K,class V>
void RBtree<K, V>::deleteNode(RBtreeNode<K, V>* p)
{
    
    // 有两个子节点的情况,先进行节点替换
    if ( p->left != nullptr && p->right != nullptr)
    {
        // 使用后续节点
        RBtreeNode<K, V>* p_successor = successor(p); 
        // 使用前继节点
        // RBtreeNode<K, V>* p_predecessor = predecessor(p); 
        // 使用前继或者后续的值来替代原节点的值
        p->key = p_successor->key;
        p->value = p_successor->value;
        p = p_successor; //指向要删除节点
    } 
    // 节点替换后,情况3就变成了1和2两种情况,后继节点为便为2-3-4树的叶子节点
    // 删除节点看看有没有左右子树
    RBtreeNode<K, V>* replacement = p->left != nullptr ? p->left : p->right;
    if (replacement != nullptr) // 删除节点有子树
    {
		replacement->parent = p->parent; //先把父亲绑上
		if (p->parent == nullptr) // 说明p是根节点,用replacement来替代根节点
		{
			this->root = replacement;
		}
		else if (p == p->parent->left)
		{
			p->parent->left = replacement;
		}
		else if (p == p->parent->right)
		{
			p->parent->right = replacement;
		}
        // 若被删除颜色为黑色,因为破坏了平衡
        // 所以此时需要调整
		if (p->color == BLACK)
		{
            // 此时改颜色就好了
			fixAfterdelete(replacement);
		}

		p->parent = nullptr;
		p->left = nullptr;
		p->right = nullptr;
        
    }
    else if (replacement == nullptr)//若没有子节点则说明替代节点为叶子节点
    {
		// 因为要保证黑色平衡,所以若删除节点是黑色要先调整
        // 先听课,因为这里的操作我还不明白
		if (p->color == BLACK)
		{
			fixAfterdelete(p);
		}
		//再删除
		//这里多添加根节点的判断是因为经过fixAfterRemove()之后
		//p可能变成根节点
		if (p->parent != nullptr)
		{
			if (p == p->parent->left)
			{
				p->parent->left = nullptr;
			}
			else if (p == p->parent->right)
			{
				p->parent->right = nullptr;
			}
			p->parent = nullptr; // 双向清空
		}
       
    }
    else if (p->parent == nullptr) // 删除节点就是根节点
    {
        this->root = nullptr;
    }
}
template<class K,class V>
void RBtree<K, V>::fixAfterdelete(RBtreeNode<K, V>* p)
{
    while (p != root && colorof(p)==BLACK) // p为根节点的时候直接退出循环
    {
        //先判断p是父节点的左子还是右子
        if (p->parent->left == p) // 若是左子树
        {
            RBtreeNode<K, V>* rnode = p->parent->right;
            if (rnode->color == RED)// 若兄弟为红色,说明该节点是假兄弟节点(其实一定会有兄弟的,所以不需要判断兄弟的有无(因为2-3-4树的性质))
            {
                // 当兄弟节点是红色的时候,其恰好为假兄弟节点
                // 那么就需要做相应的处理,用以查找真正的兄弟节点
                // 此时在2-3-4树中,其父节点为一黑一红的3节点,红色便为p的假兄弟节点
                // 因为是三节点树故必须有三个子节点,当我自身父节点的右子节点为假兄弟节点时
                // 说明其他两个子节点连接在红色的下面,此时只需要左旋即可
                //        x(黑)
                //       /   \ 
                //      p(黑) 假兄弟节点(红)
                //            /      \ 
                //          a(黑)     b(黑)    其中a,b为真兄弟节点
                rnode->parent->color = RED;
                rnode->color = BLACK;
                leftRotate(rnode->parent); //左旋
                // 找到真正的兄弟节点
                rnode = p->parent->right;
            }
            //找兄弟节点借,若兄弟节点没的借
            if (rnode->left->color==BLACK && rnode->right->color== BLACK)
            {
                //没的借要进行递归
                rnode->color = RED;
                p = p->parent;
            }
            else //若兄弟节点有的借
            {
                if (rnode->right->color==BLACK)//若右节点空。则左节点一定有东西,此时需要旋转处理
                {
                    //旋转前先变色
                    rnode->left->color = BLACK;
                    rnode->color = RED;
                    //旋转
                    rightRotate(rnode);
                }
                // 三四节点都要经历这种变色和旋转
                rnode->color = rnode->parent->color;
                rnode->parent->color = BLACK;
                rnode->right->color = BLACK;
                // 旋转
                leftRotate(rnode->parent);
                // 有的借之后便可以直接结束循环
                p = root; //将指针赋给根节点结束循环
            }
        }else//若是右子节点
        {
			RBtreeNode<K, V>* lnode = p->parent->left;
			if (lnode->color == RED)// 若兄弟为红色,其实一定会有兄弟的,所以不需要判断兄弟的有无(因为2-3-4树的性质)
			{
                lnode->parent->color = RED;
                lnode->color = BLACK;
				rightRotate(lnode->parent); //左旋
				// 找到真正的兄弟节点
                lnode = p->parent->left;
			}
			//找兄弟节点借,若兄弟节点没的借
			if (lnode->right->color == BLACK && lnode->left->color == BLACK)
			{
				//情况复杂,等下再写
				lnode->color = RED;
				p = p->parent;
			}
			else //若兄弟节点有的借
			{
				if (lnode->left->color == BLACK)//若右节点空。则左节点一定有东西,此时需要旋转处理
				{
					//旋转前先变色
                    lnode->right->color = BLACK;
                    lnode->color = RED;
					//旋转
					leftRotate(lnode);
				}
				// 三四节点都要经历这种变色和旋转
                lnode->color = lnode->parent->color;
                lnode->parent->color = BLACK;
                lnode->left->color = BLACK;
				// 旋转
				rightRotate(lnode->parent);
				// 有的借之后便可以直接结束循环
				p = root; //将指针赋给根节点结束循环
			}
        }
    }
    // 若为红色的情况则直接变黑
    p->color = BLACK;
}


版权声明:本文为qq_42138448原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。