C++ 2-3树实现(红黑树)学习笔记
最近学习了红黑树,想按照自己的理解写一篇博客,捋一捋思路,不知道正确与否,还请批评指正,感谢嘤嘤嘤,代码参考《算法》第四版。
在使用二叉查找树时,二叉查找树的运行时间取决于树的形状,树的形状取决于插入数据的顺序,最好的情况是叶子节点到根节点到距离为LogN。
2-3树可以允许“每个节点保存2个数据”的情况发生,从而提高了二叉查找树的灵活性,最终实现了无论以什么顺序插入,都可以保持树的平衡性和有序性。
一. 2-3树的节点类型
本次通过红黑树实现2-3树,在树进行操作的过程中,2-3树会出现三类节点:
(1) 2-节点,该节点有一个键,有左右两个链接(节点),以其左节点为根节点的左子树的键都小于这个2-节点的键,右键同理。
(2) 3-节点,该节点有两个键,为方便叙述,假设这两键为A和B,3-节点有三个子节点,以其左边子节点为根节点的左子树的键都小于A键,以中间子节点为根节点的形成的子树的所有键 大于A键而小于B键,以右边子节点为根节点而形成的子树的所有键都大于B键。
(3) 4-节点,这类节点不会永远存在于树当中,个人理解只是作为临时性的,为方便维持树平衡性而设立的一种节点。 4-节点有三个键A、B、C,而有四个节点(假设从左到右分别为1、2、3、4号子节点)以1号节点为根节点的子树上所有的键都小于A键;以2号节点为根节点为子树上所有的键都大于A键而小于B键;以3号节点为根节点为子树上所有的键都大于B键而小于C键;以4号节点为根节点为子树上所有的键都大于C键;
二. 红黑树
本文使用红黑树来实现2-3树,让其在保持有序性和平衡性的同时,提高查找效率。
(1) 红链接和黑链接(因为在学习算法时看的教材是《算法》第四版,所以习惯用红链接和黑链接这种名字,其实意思差不多)
为表征一个3-节点,使用红色链接来连接3-节点概念当中的两个键,而2-节点作为普通节点只有一个键,这个键的父链接就是黑色,因此,我们在实现红黑树时,可以在节点类中额外维护一个颜色变量,以方便表示2-3树中的2-节点、3-节点,甚至4-节点。
(2) 红黑树的一种定义:
🌲 红链接都是左链接
🌲 红黑树是完美黑色平衡的,即任意空链接到根节点的路径上黑链接数量相同
🌲 没有任何一个节点可以同时和两条红链接相连
🌲 一个节点的父链接只有一种颜色,非黑即红
(3) 红黑树的节点定义:
红黑树的节点定义只是比普通的二叉查找树多一个颜色定义,代码如下:
static const bool RED = true;
static const bool BLACK = false;
template<typename KEY, typename VALUE> class RBTNode
{
public:
KEY m_key;
KEY m_value;
RBTNode *RightNode = NULL;
RBTNode *LeftNode = NULL;
int m_N;
bool m_color;
RBTNode(KEY key, VALUE value, int N, bool m_color );
bool isRed(RBTNode<KEY, VALUE> *rbtnode);
};
//成员方法和构造方法实现
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>::RBTNode(KEY key, VALUE value, int N, bool color)
{
m_key = key;
m_value = value;
m_N = N;
m_color = color;
}
template<KEY key, VALUE value> bool RBTNode<KEY, VALUE>::isRed(RBTNode<KEY, VALUE> *rbtnode)
{
return rbtnode->m_color == RED;
}
isRed
方法判断该节点的颜色是否为红色(描述当前节点父链接的颜色),该节点类提供一个初始化节点的方法。
(4) 红黑树的基本操作:
在操作红黑树的过程中,涉及到三个使用很频繁的方法,分别是”使作为左链接的红链接 成为右链接“、”使作为右链接的红链接成为左链接“、”改变父链接和左右子链接的颜色“,刚开始看到这三种方法实现的功能时还无法想象到底应用场景是什么,学习后来的删除和插入操作之后才有所了解。这里将这三个方法作为红黑树类的成员函数。
🌲 红链接左旋转
假设节点1的右子节点2是红色,现在要做的就是将2作为上层节点,而把1作为2的左子节点,并保持连接他们的链接依然是红色,这样既保持了树的有序性,也实现了红链接的左旋转。
操作细节可以描述为:首先将2节点的左子树作为1节点的右子树(没有破坏有序性);将2节点的左子节点设为1节点,顺便把1节点的颜色赋给2节点,将1节点的颜色变为红色;重新设置1和2各自节点数量属性,将1的N赋值给2节点的N,然后重新计算1的N值。这里为三个链接标了ABC三个名字,方便看到旋转后各自所处的位置变化。
代码如下:
template<KEY key, VALUE value> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::rotateLeft(RBTNode<KEY, VALUE> *rbtnode)
{
RBTNode<KEY, VALUE>* B = rbtnode->RightNode;
rbtnode->Rightnode = B->LeftNode;
B->Leftnode = rbtnode;
B->m_color = rbtnode->m_color;
rbtnode->m_color = RED;
B->m_N = rbtnode->m_N;
rbtnode->m_N = size(rbtnode->LeftNode) + 1 + size(rbtnode->RightNode);
return B;
}
🌲 红链接右旋转
原理相同,不多赘述
template<KEY key, VALUE value> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::rotateRight(RBTNode<KEY, VALUE> *rbtnode)
{
RBTNode<KEY, VALUE> *B = rbtnode->LeftNode;
rbtnode->LeftNode = B->RightNode;
B->RightNode = rbtnode;
B->m_color = rbtnode->m_color;
rbtnode->m_color = RED;
B->m_N = rbtnode->m_N;
rbtnode->m_N = size(rbtnode->Leftnode) + 1 + size(rbtnode->RightNode)//size函数返回节点的m_N
}
🌲 颜色翻转
该方法是改变当前节点的颜色以及左右子节点的颜色。当前节点的颜色需要和子节点的颜色相反
template<typename KEY, typename VALUE> void RBT<KEY, VALUE>::flipcolor(RBTNode<KEY, VALUE> *rbtnode)
{
rbtnode->m_color = (rbtnode->m_color == BLACK ? RED : BLACK);
rbtnode->RightNode->m_color = (rbtnode->RightNode->m_color == BLACK ? RED : BLACK);
rbtnode->LeftNode->m_color = (rbtnode->LeftNode->m_color == BLACK ? RED : BLACK);
}
(5) 红黑树的插入
插入操作稍复杂一些,需要考虑几种情况,不过总的来说,我们还是从根节点往下深入,直到找到合适的插入位置,插入即可。但通常我们设置新插入的节点颜色为红色,为了满足红黑树的平衡以及定义,需要考虑以下几个情况:
🌲 在叶子节点插入,叶子节点有两个黑链接,如果需要插入到叶子节点的左节点位置,直接以红左链接插入就行,不影响树的平衡性;
如果需要插入到叶子节点右节点位置,先以红右链接插入,这时,违反了上面的定义,不需要存在一个红右链接,这个时候左旋转就可以了,代码如下:
if(!isRed(rbtnode->LeftNode) && isRed(rbtnode->RightNode))
{
rbtnode = rotateLeft(rbtnode);
}
🌲在一个3-结点中插入,这有三种情况:
第一种情况,如果在该位置插入,新键是和3-节点中的两个键相比是最大的,会使得节点的两个子节点颜色都是红色,需要翻转颜色,把两个子节点颜色都变黑。
if(isRed(rbtnode->RightNode) && isRed(rbtnode->LeftNode))
{
flipColors(rbtnode);
}
第二种情况,如果在该位置插入,新键是和3-节点中的两个键相比是最小的,当前节点的左节点的颜色以及左节点的左节点的颜色都是红色,需要对左节点进行右旋转,使得左节点变为在当前节点、左节点、左节点的左节点 三个节点中的最高层,而“当前节点” 和 左节点的 左节点 会变成 左右子节点。然后情况转化为第一种情况,处理即可。
if(isRed(rbtnode->LeftNode) && isRed(rbtnode->LeftNode->LeftNode))
{
rbtnode = rotateRight(rbtnode);
}
第三种情况,如果在该位置插入,新键是和3-节点中的两个键相比,在两个键中间,会插入到较小节点的右子节点,此时因为右子节点是红色,需要进行左旋转,这样又回到了第二种情况,按照第二种情况处理。这三种情况算法书中有一张很好的图可以参考,这里用红框标出了新插入的元素,篮框表示另一种插入位置的情况。
if(!isRed(rbtnode->LeftNode)&&isRed(rbtnode->RightNode))
rbtnode = rotateLeft(rbtnode);
三种情况以及整体put逻辑捋清楚后,就可以写出put函数了,如下所示:
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::put(RBTNode<KEY, VALUE> *rbtnode, KEY key, VALUE value)
{
if(rbtnode == NULL)
return new RBTNode<KEY, VALUE>(key, value, 1, RED);
if(key < rbtnode->m_key)
rbtnode->LeftNode = put(rbtnode->LeftNode, key, value);
if(key > rbtnode->m_key)
rbtnode->RightNode = put(rbtnode->RightNode, key, value);
else rbtnode->m_value = value;
if(!isRed(rbtnode->LeftNode) && isRed(rbtnode->RightNode))
rbtnode = rotateLeft(rbtnode);
if(isRed(rbtnode->LeftNode) && isRed(rbtnode->LeftNode->LeftNode))
rbtnode = rotateRight(rbtnode);
if(isRed(rbtnode->LeftNode) && isRed(rbtnode->RightNode))
flipColors(rbtnode);
}
(6) 红黑树删除
红黑树删除操作比较复杂,可以先学习删除最大键和删除最小键两种操作,最后结合这两种操作理解红黑树删除指定键的方法。
🌲 红黑树删除最小键
删除最小键和普通的二叉查找树相比,也是需要一直向左子树遍历。但不同的是,需要考虑一下,如果最小键是个3-节点,那么直接删除,不会影响平衡性。如果最小键是2-节点,直接删除就会失去平衡性,所以在往下遍历的过程中,让当前节点都变为3-节点,那么可以保证最后一个节点就一定是3-节点,然后直接删除即可。
但在这个过程中可能会产生4-节点,即当前节点的两个左右节点颜色都是红色,可以暂时不管它,直到删除最小键操作完成之后,再自底向上分解4-节点,使红黑树恢复正常状态。
另一个问题产生了,如何让当前节点变为3-节点? 从父节点开始考虑问题,如果rbtnode的左节点的左节点 和rbtnode 的左节点都是黑色(当然r btnode的右节点也应该是黑色),可以通过翻转颜色让当前节点变成4-节点,然后判断一下rbtnode的右节点的左节点是不是红色的
如果是,可以在不影响平衡性的情况下,让该节点变为父节点,让rbtnode 变为左节点,让rbtnode的右节点仍然为右节点,相当于借了一个节点给rbtnode的左节点的左节点和 它以红链接相连。
如果不是,变成4-节点也可以保证当前节点是不是2-节点。所以红黑树删除最小键的代码如下所示:
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::deleteMin(RBTNode<key, VALUE> *rbtnode)
{
if(rbtnode->LeftNode == NULL)
{
delete rbtnode;
return NULL;
}
if(!isRed(rbtnode->LeftNode) && !isRed(rbtnode->LeftNode->LeftNode))
rbtnode = moveRedLeft();
rbtnode->LeftNode = deleteMin(rbtnode->LeftNode);
return balance(rbtnode);
}
template<typename KEY, typename VALUE> void RBT<KEY, VALUE>::moveRedLeft(RBTNode<key, VALUE> *rbtnode)
{
flipColors(rbtnode);
if(isRed(rbtnode->RightNode->LeftNode))
{
rbtnode->RightNode = rotateRight(rbtnode->RightNode);
rbtnode = rotateLeft(rbtnode);
flipColors(rbtnode);
}
return rbtnode;
}
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::balance(RBTNode<KEY, VALUE> *rbtnode)
{
if(isRed(rbtnode->RightNode)) rbtnode = rotateLeft(rbtnode);
if(isRed(rbtnode->LeftNode) && isRed(rbtnode->LeftNode->LeftNode))
rbtnode = rotateRight(rbtnode);
if(isRed(rbtnode->LeftNode) && isRed(rbtnode->RightNode))
flipColors_delete(rbtnode);
rbtnode->m_N = size(rbtnode->LeftNode) + 1 + size(rbtnode->RightNode);
return rbtnode;
}
🌲 删除最大键
删除最大键的思路和删除最小键比较像,但还是有点差别。既然是删除最大键,那就是要向右子树不停地遍历,知道找到一个右子节点为空的节点,说明这个节点就是最小节点。但还需要分情况讨论,因为不管红黑链接直接这样做可能会破坏平衡性。
如果通过不断地遍历右子树已经找到了最大键,这个最大键的右子节点为空(黑链接)并且这个最大键的颜色(父链接的颜色)为红色,那么直接删除并不会破坏平衡性,毕竟我们要求的是红黑树是完美黑色平衡的
。
既然在最大键为红色的情况下删除没什么问题,那在向右子树遍历的过程中,我们使当前节点的右子节点的颜色为红就可以。
首先,如果在深入右子树的过程中,发现当前节点的左节点为红色但右节点不是红色,那么右旋转当前节点。这个操作可以使这个较大的节点为红色。
如果当前节点的右节点为黑色,右节点的左节点也是黑色,那么我们要么翻转颜色,要么考虑借一个键使得当前节点的右子节点为红色,并下降一层(和删除最小键思路差不多),但这里判断右节点的左节点的原因是,右节点不可能为红色。
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE> *rbtnode RBT<KEY, VALUE>::moveRedRight(RBTNode<KEY, VALUE> *rbtnode)
{
flipColors(rbtnode);
if(isRed(rbtnode->LeftNode->LeftNode))
{
rbtnode->rotateRight(rbtnode);
flipColors(rbtnode);
}
return rbtnode;
}
templat<typename KEY, typename VALUE> RBTNode<KEY, VALUE> *rbtnode RBT<KEY, VALUE>::deleteMax(RBTNode<KEY, VALUE> *rbtnode)
{
if(isRed(rbtnode->LeftNode))
rbtnode = rotateRight(rbtnode);
if(rbtnode->RightNode == NULL)
{
delete rbtnode;
return NULL;
}
if(!isRed(rbtnode->RightNode) && !isRed(rbtnode->RightNode->LeftNode))
moveRedRight(rbtnode);
rbtnode->RightNode = deleteMax(rbtnode->RightNode);
return balance(rbtnode);
}
🌲 删除指定键
理解了deleteMin 和 deleteMax,删除指定键就好写了,在删除前,可以先进行查找,看看指定的键在不在树中,如果不在直接返回就好。
先把查找的代码列出来
template<typename KEY, typename VALUE> VALUE RBT<KEY, VALUE>::get(RBTNode<KEY, VALUE> *rbtnode, KEY key)
{
while (rbtnode != NULL) {
if(key < rbtnode->m_key)
rbtnode = rbtnode->LeftNode;
else if(key > rbtnode->m_key)
rbtnode = rbtnode->RightNode;
else return rbtnode->m_value;
}
return static_cast<int>(0);
}
template<typename KEY, typename VALUE> bool RBT<KEY, VALUE>::contains(KEY key)
{
return get(key) != 0;
}
确定指定键存在于树当中后,就可以从根节点开始深入,如果指定键小于当前节点的键,就做删除最小键的部分步骤,即保证当前节点是红色,如果指定键大于当前节点键,那就实现删除最大键在实行递归前的部分算法,即保证右节点为红色,方便删除。如果指定键等于当前节点键,做法同上一篇整理的普通二叉树很像,找出右子树中的最小键和当前要删除的键替换,然后将问题转化为删除右子树中的最小键。最后使用balance自底向上配平4-节点。
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::delete_RBTNode(RBTNode<KEY, VALUE> *rbtnode, KEY key)
{
if(key < rbtnode->m_key)
{
//如果指定键小于 当前节点的键 ,首先保证当前节点或当前左子节点不是2-节点,从父节点开始判断
if(!isRed(rbtnode->LeftNode) && !isRed(rbtnode->LeftNode-> LeftNode))
rbtnode = moveRedLeft(rbtnode);
rbtnode->LeftNode = delete_RBTNode(rbtnode->LeftNode, key);//继续对🌲遍历
}
else
{
if(isRed(rbtnode->LeftNode))//判断左子节点是否为红色,如果是红色就向右旋转
rbtnode = rotateRight(rbtnode);
if(key == rbtnode->m_key && (rbtnode->RightNode == NULL))
//判断待删除的节点是否为当前节点,且右子节点是否为空,此时待删除的节点为叶子节点,这里只需要判断右子节点是否为NULL即可,因为即使如果原来查找到的键所在节点是左侧的叶子节点,也会因为上面的判断条件而变为右叶子节点
{
delete rbtnode;
return NULL;
}
if(!isRed(rbtnode->RightNode) && !isRed(rbtnode->RightNode->LeftNode))
rbtnode = moveRedRight(rbtnode);
if(key == rbtnode->m_key)
{
RBTNode<KEY, VALUE> *x = min(rbtnode->RightNode);
rbtnode->m_key = x->m_key;
rbtnode->m_value = x->m_value;
rbtnode->RightNode = deleteMin(rbtnode->RightNode);
}
else rbtnode->RightNode = delete_RBTNode(rbtnode->RightNode, key);
}
return balance(rbtnode);
}
下面是完整hpp代码(图方便全写在hpp里了,惭愧)
//
// RedBlackBST
// Dat_Structure
//
// Created by 云子谣 on 2020/3/19.
// Copyright © 2020 云子谣. All rights reserved.
//
#ifndef RedBlackBST_hpp
#define RedBlackBST_hpp
#include <stdio.h>
#include <iostream>
#endif /* RedBlackBST_hpp */
static const bool RED = true;
static const bool BLACK = false;
using namespace std;
template<typename KEY, typename VALUE> class RBTNode
{
public:
KEY m_key;
VALUE m_value;
RBTNode *LeftNode = NULL;
RBTNode *RightNode = NULL;
int m_N;
bool m_color;
RBTNode(KEY key, VALUE value, int N, bool m_color);
bool isRed(RBTNode<KEY, VALUE> *rbtnode);
};
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>::RBTNode(KEY key, VALUE value, int N, bool color)
{
m_key = key;
m_value = value;
m_N = N;
m_color = color;
}
template<typename KEY, typename VALUE> bool RBTNode<KEY, VALUE>::isRed(RBTNode<KEY, VALUE> *rbtnode)
{
if(rbtnode == NULL) return false;
return rbtnode->m_color == RED;
}
template<typename KEY, typename VALUE> class RBT
{
private:
int size(RBTNode<KEY, VALUE> *rbtnode);
RBTNode<KEY, VALUE> *m_root = NULL;
RBTNode<KEY, VALUE>* rotateLeft(RBTNode<KEY, VALUE> *rbtnode);
RBTNode<KEY, VALUE>* rotateRight(RBTNode<KEY, VALUE> *rbtnode);
void flipColors(RBTNode<KEY, VALUE> *rbtnode);
void flipColors_delete(RBTNode<KEY, VALUE> *rbtnode);
RBTNode<KEY, VALUE>* put(RBTNode<KEY, VALUE>* rbtnode, KEY key, VALUE value);
bool isRed(RBTNode<KEY, VALUE> *rbtnode);
RBTNode<KEY, VALUE>* moveRedLeft(RBTNode<KEY, VALUE> *rbtnode);
RBTNode<KEY, VALUE>* moveRedRight(RBTNode<KEY, VALUE> *rbtnode);
RBTNode<KEY, VALUE>* deleteMin(RBTNode<KEY, VALUE> *rbtnode);
RBTNode<KEY, VALUE>* deleteMax(RBTNode<KEY, VALUE> *rbtnode);
RBTNode<KEY, VALUE>* balance(RBTNode<KEY, VALUE> *rbtnode);
void print_all(RBTNode<KEY, VALUE> *rbtnode);
VALUE get(RBTNode<KEY, VALUE> *rbtnode, KEY key);
RBTNode<KEY, VALUE>* delete_RBTNode(RBTNode<KEY, VALUE> *rbtnode, KEY key);
RBTNode<KEY, VALUE>* min(RBTNode<KEY, VALUE> *rbtnode);
public:
KEY min();
int size();
void put(KEY key, VALUE value);
void deleteMin();
void deleteMax();
void print_all();
int check_size();
VALUE get(KEY key);
bool contains(KEY key);
void delete_RBTNode(KEY key);
};
//min
template<typename KEY, typename VALUE> KEY RBT<KEY, VALUE>::min()
{
return min(m_root)->m_key;
}
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::min(RBTNode<KEY, VALUE> *rbtnode)
{
if(rbtnode->LeftNode == NULL) return rbtnode;
else return min(rbtnode->LeftNode);
}
//delete
template<typename KEY, typename VALUE> void RBT<KEY, VALUE>::delete_RBTNode(KEY key)
{
if (!isRed(m_root->LeftNode) && !isRed(m_root->RightNode)) {
m_root->m_color = RED;
}
m_root = delete_RBTNode(m_root, key);
if(m_root != NULL) m_root->m_color = BLACK;
}
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::delete_RBTNode(RBTNode<KEY, VALUE> *rbtnode, KEY key)
{
if(key < rbtnode->m_key)
{
//如果指定键小于 当前节点的键 ,首先保证当前节点或当前左子节点不是2-节点,从父节点开始判断
if(!isRed(rbtnode->LeftNode) && !isRed(rbtnode->LeftNode-> LeftNode))
rbtnode = moveRedLeft(rbtnode);
rbtnode->LeftNode = delete_RBTNode(rbtnode->LeftNode, key);//继续对🌲遍历
}
else
{
if(isRed(rbtnode->LeftNode))//判断左子节点是否为红色,如果是红色就向右旋转
rbtnode = rotateRight(rbtnode);
if(key == rbtnode->m_key && (rbtnode->RightNode == NULL))
//判断待删除的节点是否为当前节点,且右子节点是否为空,此时待删除的节点为叶子节点,这里只需要判断右子节点是否为NULL即可,因为即使如果原来查找到的键所在节点是左侧的叶子节点,也会因为上面的判断条件而变为右叶子节点
{
delete rbtnode;
return NULL;
}
if(!isRed(rbtnode->RightNode) && !isRed(rbtnode->RightNode->LeftNode))
rbtnode = moveRedRight(rbtnode);
if(key == rbtnode->m_key)
{
RBTNode<KEY, VALUE> *x = min(rbtnode->RightNode);
rbtnode->m_key = x->m_key;
rbtnode->m_value = x->m_value;
rbtnode->RightNode = deleteMin(rbtnode->RightNode);
}
else rbtnode->RightNode = delete_RBTNode(rbtnode->RightNode, key);
}
return balance(rbtnode);
}
//contains
template<typename KEY, typename VALUE> bool RBT<KEY, VALUE>::contains(KEY key)
{
return get(key) != 0;
}
// get
template<typename KEY, typename VALUE> VALUE RBT<KEY, VALUE>::get(KEY key)
{
return get(m_root, key);
}
template<typename KEY, typename VALUE> VALUE RBT<KEY, VALUE>::get(RBTNode<KEY, VALUE> *rbtnode, KEY key)
{
while (rbtnode != NULL) {
if(key < rbtnode->m_key)
rbtnode = rbtnode->LeftNode;
else if(key > rbtnode->m_key)
rbtnode = rbtnode->RightNode;
else return rbtnode->m_value;
}
return static_cast<int>(0);
}
template<typename KEY, typename VALUE> int RBT<KEY, VALUE>::check_size()
{
if(m_root == NULL) return 0;
else return m_root->m_N;
}
//isRed
template<typename KEY, typename VALUE> bool RBT<KEY, VALUE>::isRed(RBTNode<KEY, VALUE> *rbtnode)
{
if(rbtnode == NULL)
return false;
return rbtnode->m_color == RED;
}
//size
template<typename KEY, typename VALUE> int RBT<KEY, VALUE>::size()
{
return size(m_root);
}
template<typename KEY, typename VALUE> int RBT<KEY, VALUE>::size(RBTNode<KEY, VALUE> *rbtnode)
{
if(rbtnode == NULL) return 0;
else return rbtnode->m_N;
}
//rotate
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::rotateLeft(RBTNode<KEY, VALUE> *rbtnode)
{
RBTNode<KEY, VALUE>* x = rbtnode->RightNode;
rbtnode->RightNode = x->LeftNode;
x->LeftNode = rbtnode;
x->m_color = rbtnode->m_color;
rbtnode->m_color = RED;
x->m_N = rbtnode->m_N;
rbtnode->m_N = 1 + size(rbtnode->LeftNode) + size(rbtnode->RightNode);
return x;
}
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::rotateRight(RBTNode<KEY, VALUE> *rbtnode)
{
RBTNode<KEY, VALUE>* x = rbtnode->LeftNode;
rbtnode->LeftNode = x->RightNode;
x->RightNode = rbtnode;
x->m_color = rbtnode->m_color;
rbtnode->m_color = RED;
x->m_N = rbtnode->m_N;
rbtnode->m_N = 1 + size(rbtnode->LeftNode) + size(rbtnode->RightNode);
return x;
}
template<typename KEY, typename VALUE> void RBT<KEY, VALUE>::flipColors(RBTNode<KEY, VALUE> *rbtnode)
{
rbtnode->m_color = RED;
rbtnode->RightNode->m_color = BLACK;
rbtnode->LeftNode->m_color = BLACK;
}
//put
template<typename KEY, typename VALUE> void RBT<KEY, VALUE>::put(KEY key, VALUE value)
{
m_root = put(m_root, key, value);
m_root->m_color = BLACK;
}
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::put(RBTNode<KEY, VALUE>* rbtnode, KEY key, VALUE value)
{
if(rbtnode == NULL)
return new RBTNode<KEY, VALUE>(key, value, 1, RED);
if(key < rbtnode->m_key) rbtnode->LeftNode = put(rbtnode->LeftNode, key, value);
else if (key > rbtnode->m_key) rbtnode->RightNode = put(rbtnode->RightNode, key, value);
else rbtnode->m_value = value;
if(!isRed(rbtnode->LeftNode)&&isRed(rbtnode->RightNode))
rbtnode = rotateLeft(rbtnode);
if(isRed(rbtnode->LeftNode) && isRed(rbtnode->RightNode))
flipColors(rbtnode);
if(isRed(rbtnode->LeftNode) && isRed(rbtnode->LeftNode->LeftNode))
rbtnode = rotateRight(rbtnode);
rbtnode->m_N = size(rbtnode->LeftNode) + 1 + size(rbtnode->RightNode);
return rbtnode;
}
template<typename KEY, typename VALUE> void RBT<KEY, VALUE>::deleteMin()
{
if(!isRed(m_root->LeftNode) && !isRed(m_root->RightNode)) //如果根节点的左子节点的color不是red,右子节点的color也不是red
m_root->m_color = RED;
m_root = deleteMin(m_root);
if(m_root != NULL)
m_root->m_color = BLACK;
}
//deleteMin
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::deleteMin(RBTNode<KEY, VALUE> *rbtnode)
{
if(rbtnode->LeftNode == NULL)
{
delete rbtnode;
return NULL;
}//如果当前节点的左节点为空,说明当前节点就是最小的节点,返回NULL,即删除当前节点。
if(!isRed(rbtnode->LeftNode) && !isRed(rbtnode->LeftNode->LeftNode))
//如果当前节点的左子节点color 为black, 而当前节点左子节点的左子节点的color 也是black
moveRedLeft(rbtnode);
rbtnode->LeftNode = deleteMin(rbtnode->LeftNode);
return balance(rbtnode);
}
//flipcolor
template<typename KEY, typename VALUE> void RBT<KEY, VALUE>::flipColors_delete(RBTNode<KEY, VALUE> *rbtnode)
{
rbtnode->m_color = (rbtnode->m_color == BLACK ? RED : BLACK);
rbtnode->RightNode->m_color = (rbtnode->RightNode->m_color == BLACK ? RED : BLACK);
rbtnode->LeftNode->m_color = (rbtnode->LeftNode->m_color == BLACK ? RED : BLACK);
}
//moveRedLeft
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::moveRedLeft(RBTNode<KEY, VALUE> *rbtnode)
{
//如果当前节点的左子节点color 为black, 而当前节点左子节点的左子节点的color 也是black
flipColors_delete(rbtnode);
//rbtnode的左子节点和右子节点的color都变成red,rbtnode的color变为black
//如果用例中的树只有三个节点,此时已经可以返回一个4-节点
if(isRed(rbtnode->RightNode->LeftNode))//如果当前节点的右子节点的左子节点的color是red
{
rbtnode->RightNode = rotateRight(rbtnode->RightNode);
//旋转rbtnode->RightNode 和 rbtnode->Rightnode->LeftNode的位置,以免形成rbtnode->RightNode是red,rbtnode->RightNode->LeftNode也是红色的情况
rbtnode = rotateLeft(rbtnode);
flipColors_delete(rbtnode);//调整红黑链接
}
return rbtnode;
}
//balance
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::balance(RBTNode<KEY, VALUE> *rbtnode)
{
if(isRed(rbtnode->RightNode)) rbtnode = rotateLeft(rbtnode);
if(isRed(rbtnode->LeftNode) && isRed(rbtnode->LeftNode->LeftNode))
rbtnode = rotateRight(rbtnode);
if(isRed(rbtnode->LeftNode) && isRed(rbtnode->RightNode))
flipColors_delete(rbtnode);
rbtnode->m_N = size(rbtnode->LeftNode) + 1 + size(rbtnode->RightNode);
return rbtnode;
}
template<typename KEY, typename VALUE> void RBT<KEY, VALUE>::print_all()
{
print_all(m_root);
}
template<typename KEY, typename VALUE> void RBT<KEY, VALUE>::print_all(RBTNode<KEY, VALUE> *rbtnode)
{
if(rbtnode == NULL) return;
print_all(rbtnode->LeftNode);
cout << rbtnode->m_key << endl;
print_all(rbtnode->RightNode);
}
//deleteMax
template<typename KEY, typename VALUE> void RBT<KEY, VALUE>::deleteMax()
{
if(m_root == NULL)
{
cout << "🌲为空" << endl;
return;
}
if(!isRed(m_root->LeftNode) && !isRed(m_root->RightNode))
m_root->m_color = RED;//此步骤不明白
m_root = deleteMax(m_root);
if(m_root != NULL)
m_root->m_color = BLACK;
}
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::deleteMax(RBTNode<KEY, VALUE> *rbtnode)
{
if(isRed(rbtnode->LeftNode))
rbtnode = rotateRight(rbtnode); //保证最大键是红节点
if(rbtnode->RightNode == NULL)
{
delete rbtnode;
return NULL;
}
if(!isRed(rbtnode->RightNode) && !isRed(rbtnode->RightNode->LeftNode))
//此处之所以不判断rbtnode->RightNode->RightNode是因为rbtnode->RightNode->RightNode此时不可能是红色的,但在往下深入的过程中,会遇到判断LeftNode是否为红色,是就旋转,因此判断rbtnode->RightNode->LeftNode的效果就是判断当前节点是否为红色或者当前节点的右节点是否为红色
rbtnode = moveRedRight(rbtnode);
rbtnode->RightNode = deleteMax(rbtnode->RightNode);
return balance(rbtnode);
}
//moveRedRight
template<typename KEY, typename VALUE> RBTNode<KEY, VALUE>* RBT<KEY, VALUE>::moveRedRight(RBTNode<KEY, VALUE> *rbtnode)
{
flipColors_delete(rbtnode);
if(isRed(rbtnode->LeftNode->LeftNode))
{
rbtnode = rotateRight(rbtnode);
flipColors_delete(rbtnode);
}
return rbtnode;
}