(一)了解二叉搜索树的删除操作
删除操作总结:
******普通结点删除:*******
①删除叶结点
②删除只有1个子结点的结点
>>如果被删除结点的左子树为空,则令其右子树子承父业代替其位置即可
>>如果被删除结点的右子树为空,则令其左子树子承父业代替其位置即可
③删除一个有2个子结点的结点(找前驱/后继来替换待被删除的结点的值,然后判断前驱/后继是否有左右孩子,若有孩子,需要判断孩子存在,删除该替换结点需要进行的操作)
*******特殊情况:*******
④根结点无左右子树的情况
⑤删除只有一个子结点的根结点
(二)探讨红黑树与2-3-4树之间的等价关系,以及如何转换。
根据2-3-4树进行删除操作可以分为三种情况:
情况一:自己能搞定的,对应叶子节点应该是3节点和4节点
情况二:自己搞不定的,需要跟兄弟借,找父亲借,父亲下来,然后兄弟我一个人去代替父亲当家,(找兄弟借,兄弟有的借.分2种情况: 兄弟节点本来就是3节点,或者4节点的情况)
情况三:找兄弟借,兄弟没得借
问题思考:什么叫做兄弟结点呢?什么时真正的兄弟结点呢?什么叫做有得借呢?
兄弟结点:此文所指的兄弟结点是指待删除结点在2-3-4树上能找到兄弟结点
真正的兄弟结点:待删除结点在2-3-4树上找到的兄弟结点,有可能不是待删除结点在红黑树上能过找到的兄弟结点,此时需要对红黑树进行调整,才能够找到对应2-3-4树上找到的兄弟结点。
兄弟有得借:是指该兄弟结点存在红色的子结点,可以被借走。
兄弟没得借:若该兄弟无左右孩子或者左右孩子的颜色都为黑色,则无法被借走,此为兄弟没得借情况。
情况一:自己能搞定的,对应叶子节点应该是3节点和4节点
》》详细分析上图
红黑树与2-3-4树的等价关系图
要对一棵红黑树进行结点删除,比如删除4结点,我们可以找到4的前驱/后继,这里找4的后继5作为替代结点,可以替换掉4结点的值。根据二叉搜索树的删除规则可知,由于5结点没有左右孩子,本可以直接删除,但是在一棵红黑树中,5结点的颜色为黑色,直接删除将会导致红黑树不平衡。此时我们需要对红黑树进行调整,然后再将其删除掉。【大致思路,后续会详细讲解各种情况】
如何解决删除结点会导致红黑树不平衡呢?我们可以从2-3-4树上找到解决思路,我们可以发现在2-3-4树,3节点和4节点里面删除一个元素是合法的,所以红黑树需要调整到与之对应的2-3-4树,此时进行删除操作就可以使得红黑树平衡。
结论:红黑树上面的删除结点一定是2-3-4树上的叶子结点
情况一详解:
红黑树中的0结点、7.5结点、9结点、11结点都可以直接被删除,因为它的颜色为红色且为叶子结点,删除后不会影响红黑树的平衡。从二叉搜索树的删除操作中来看这属于删除叶子结点的情况。从删除2-3-4树上的视角上看,是满足3节点和4节点里面删除一个元素是合法的。
红黑树中的1结点、7结点都有一个孩子结点且其为红色,所以可以将其孩子结点的值赋值给待删除的结点的值,然后将其颜色设置为黑色,这样红黑树依然保持平衡。从二叉搜索树的删除操作中来看这属于删除只有1个子结点的结点的情况。从删除2-3-4树上的视角上看,是满足3节点和4节点里面删除一个元素是合法的。
红黑树中的6结点是有左孩子和右孩子的结点且颜色为黑色,在二叉树搜索树中,删除一个有两个子结点的结点,需要找其前驱/后继的值进行替换,然后再把前驱/后继可能存在的子树挂在相应的结点上,最后再把前驱/后继删除。此处红黑树中的6结点的后继是7结点,我们发现7结点是有一个右孩子7.5且颜色为红色,所以可以将7.5结点的值赋值给7结点并设置颜色为黑色,删除掉原先的7.5结点。从删除2-3-4树上的视角上看,是满足3节点和4节点里面删除一个元素是合法的。
红黑树中8结点是有左孩子和右孩子的结点且颜色为红色,在二叉树搜索树中,删除一个有两个子结点的结点,需要找其前驱/后继的值进行替换,然后再把前驱/后继可能存在的子树挂在相应的结点上,最后再把前驱/后继删除。此处红黑树中的8结点的后继是9结点,我们发现9结点是叶子结点且为红色,所以可以将9结点的值赋值给8结点,颜色设置为红色,删除掉原先的9结点。从删除2-3-4树上的视角上看,是满足3节点和4节点里面删除一个元素是合法的。
>>核心部分代码:
//情况一、替代节点是红色,则直接染黑,补偿删除的黑色节点,这样红黑树依然保持平衡
x->color = BLACK;
情况二:自己搞不定的,需要跟兄弟借,找父亲借,父亲下来,然后兄弟我一个人去代替父亲当家,(找兄弟借,兄弟有的借.分2种情况: 兄弟节点本来就是3节点,或者4节点的情况)
》》兄弟节点本来就是3节点的情况
》》详细分析上图
例如删除5结点:
由于5结点是黑色的叶子结点,直接删除会破坏红黑树的平衡,所以属于自己搞不定,需要跟兄弟借。在2-3-4树上来看,5结点的兄弟结点是7,可以向其借子结点6.5来实现删除后调整。 但是在红黑树上来看,5结点的兄弟结点是8且颜色为红色。当红黑树上的兄弟结点颜色为红色,则说明其不是真正的兄弟结点,因为其在2-3-4树上的兄弟结点与其在红黑树上的兄弟结点的值并不一样,所以需要进行调整来寻找其真正的兄弟结点。
(一)兄弟节点本来就是3节点
>>寻找真正的兄弟结点
2-3-4树对应红黑树来看的话,首先先把6结点和8结点的颜色进行调整,6结点的颜色设置为红色,8结点的颜色设置为黑色,在红黑树看来,此时5结点可以方便找到真正的兄弟结点。
图1 图2
需要将图1的2-3-4树转为图2的2-3-4树,在图1中等价关系的红黑树中,将6结点的颜色设置为红色,将8结点设置为黑色, 由于5结点的兄弟结点是8,其是6结点的右孩子,所以需要对其进行“左旋”,便可找到真正的兄弟结点。
所以得到如下等价的红黑树和2-3-4树,5结点找到真正的兄弟结点7之后,发现7的左孩子是颜色为红色的6.5结点,可以向其借走子结点来实现红黑树删除平衡。
接下来进行颜色调整,将5结点的真正兄弟结点7的颜色设置为红色,将7结点的左孩子的颜色设置为黑色,然后进行右旋操作。继续再进行颜色调整,将6.5结点的颜色设置为父亲结点6的颜色,然后将父亲结点6和右孩子结点7设置为黑色,再进行“右旋操作”,最后把结点5删除掉,可以得到一棵平衡的红黑树。
》》兄弟节点本来就是4节点的情况
(二)兄弟节点本来就是4节点(借1个结点)
2-3-4树中4节点情况:(借走一个子结点)
我们可以看到这里的操作和3节点情况一样,都是需要找到被删除结点5结点的真正兄弟结点,然后对其进行变色->右旋->变色->左旋,这里的红黑树调整需要进行两次旋转。接下来会讲解只用一次旋转的方案,即向2-3-4树中的4节点借走两个子结点:
(二)兄弟节点本来就是4节点(借2个结点)
2-3-4树中4节点情况:(借走两个子结点)
需要找到被删除结点5结点的真正兄弟结点,然后对其进行变色->左旋,这里的红黑树调整需要进行一次旋转。相比借走1个节点,借走2个节点可以减少一次旋转操作,可以提升性能,也能使得红黑树删除平衡。
>>核心部分代码:
//情况二,找兄弟借,兄弟有的借(x是左孩子的情况)
else {
//分2种小情况:兄弟节点本来是3节点或者是4节点的情况
if (colorOf(rnode->right) == BLACK) {
//if (rnode->right == nullptr) {
rnode->left->color = BLACK;
rnode->color = RED;
rightRotate(rnode);
rnode = x->parent->right;
}
rnode->color = x->parent->color;
x->parent->color = BLACK;
if(rnode->right)
rnode->right->color = BLACK;
leftRotate(x->parent);
x = root;
}
//情况二,找兄弟借,兄弟有的借(x是右孩子的情况)
else {
//分2种小情况:兄弟节点本来是3节点或者是4节点的情况
if (colorOf(lnode->left) == BLACK) {
//if (lnode->left == nullptr) {
lnode->right->color = BLACK;
lnode->color = RED;
leftRotate(lnode);
lnode = x->parent->left;
}
lnode->color = x->parent->color;
x->parent->color = BLACK;
if(lnode->left)
lnode->left->color = BLACK;
rightRotate(x->parent);
x = root;
}
情况三:找兄弟借,兄弟没得借
>>核心部分代码:
//情况三,找兄弟借,兄弟没得借 (x是左孩子)
if (colorOf(rnode->left) == BLACK && colorOf(rnode->right) == BLACK){
//情况复杂,暂时不写
rnode->color = RED;
x = x->parent;
}
//情况三,找兄弟借,兄弟没得借(x是右孩子)
if(colorOf(lnode->left) == BLACK && colorOf(lnode->right) == BLACK){
//情况复杂,暂时不写
lnode->color = RED;
x = x->parent;
}
>> 红黑树删除核心代码:
1、找前驱代码:
/*
void predecessor(Node* node)
找到指定结点的前驱结点,即找小于node结点的最大值
*/
Node* predecessor(Node* node) {
if (node == nullptr) {
return nullptr;
}
else if (node->left != nullptr) {
Node* p = node->left;
while (p->right != nullptr) {
p = p->right;
}
return p;
}
else {
Node* p = node->parent;
Node* cur = node;
while (p != nullptr && cur == p->left) {
cur = p;
p = p->parent;
}
return p;
}
}
2、找后继代码
/*
void successor(Node* node)
找后继结点:即大于结点的最小值
*/
Node* successor(Node* node) {
if (node == nullptr) {
return nullptr;
}
else if (node->right != nullptr) {
Node* p = node->right;
while (p->left != nullptr) {
p = p->left;
}
return p;
}
else {
Node* p = node->parent;
Node* cur = node;
while (p != nullptr && cur == p->right) {
cur = p;
p = p->parent;
}
return p;
}
}
3、根据key、value获取节点
Node* getNode(const pair<k, v> kv) {
Node* node = this->root;
while (node != nullptr) {
//int cmp = key.compareTo((K)node.key);
int cmp = compareTo(kv, node);
if (cmp < 0) {
node = node->left;
}
else if (cmp > 0) {
node = node->right;
}
else
return node;
}
return nullptr;
}
4、根据key、value进行删除操作
v remove(const pair<k, v> kv) {
Node* node = getNode(kv);
if (node == nullptr) {
return "";
}
//V oldValue = (V)node.value;
v oldValue = kv.second;
deleteNode(node);
return oldValue;
}
5、删除结点
/**
* 删除操作:
* 1、删除叶子结点,直接删除
* 2、删除的结点有一个子结点,那么用子结点来替代
* 3、如果删除的节点有2个子结点,此时需要找到前驱结点或者后继结点来替代
*
*/
void deleteNode(Node* node) {
//3、node结点有2个孩子【即node是度为2的结点】
if (node->left != nullptr && node->right != nullptr) {
//后继结点替代
Node* replaceNode = successor(node);
node->kv.first = replaceNode->kv.first;
node->kv.second = replaceNode->kv.second;
node = replaceNode;
}
//node的孩子结点是度为0或1的结点
Node* replacement = node->left != nullptr ? node->left : node->right;
//2、替代结点不为空
if (replacement != nullptr) {
//替代者的父指针指向的原来node的父亲
replacement->parent = node->parent;
//node是根节点
if (node->parent == nullptr) {
root = replacement;
}
//node是左孩子,所以替代者依然是左孩子
else if (node == node->parent->left) {
node->parent->left = replacement;
}
//node是右孩子,所以替代者依然是右孩子
else {
node->parent->right = replacement;
}
//将node的左右孩子指针和父指针都指向null(此时node处于游离状态,等待垃圾回收)
node->left = node->right = node->parent = nullptr;
//替换完之后需要调整平衡
if (node->color == BLACK) {
//需要调整,这种情况一定是红色(替代节点一定是红色,此时只要变色)
fixAfterRemove(replacement);
}
delete node;
}
//删除结点就是根结点
else if (node->parent == nullptr) {
root = nullptr;
}
//1、node结点是叶子结点,replacement为nullptr
else {
//先调整
if (node->color == BLACK) {
fixAfterRemove(node);
}
//再删除
if (node->parent != nullptr) {
if (node == node->parent->left) {
node->parent->left = nullptr;
}
else if (node == node->parent->right) {
node->parent->right = nullptr;
}
node->parent = nullptr;
delete node;
}
}
}
6、完整代码:
/*
1、2-3-4树:新增元素+2节点合并(节点中只有1个元素)=3节点(节点中有2个元素)
红黑树:新增一个红色节点+黑色父亲节点=上黑下红(2节点)--------------不要调整
2、2-3-4树:新增元素+3节点合并(节点中有2个元素)=4节点(节点中有3个元素)
这里有6种情况(左3,右3,还有2个左中右不需要调整)----------------左3,右3需要调整,其余2个不需要调整
红黑树:新增红色节点+上黑下红=排序后中间节点是黑色,两边节点都是红色(3节点)
3、2-3-4树:新增一个元素+4节点合并=原来的4节点分裂,中间元素升级为父节点,新增元素与剩下的其中一个合并
红黑树:新增红色节点+爷爷节点黑,父节点和叔叔节点都是红色=爷爷节点变红,父亲和叔叔变黑,如果爷爷是根节点,则再变黑
*/
#include<iostream>
using namespace std;
#include<vector>
#include<cmath>
#include<string>
#include<vector>
#define N 100
//#include "StringBuilder.h"
enum Colour {
RED,
BLACK
};
template<class k, class v>
struct RBTreeNode {
RBTreeNode<k, v>* left;
RBTreeNode<k, v>* right;
RBTreeNode<k, v>* parent;
pair<k, v> kv;
Colour color;
RBTreeNode(const pair<k, v>& kv)
:left(nullptr)
, right(nullptr)
, parent(nullptr)
, kv(kv)
, color(RED)
{}
};
template<class k, class v>
class RBTree {
typedef RBTreeNode<k, v> Node;
public:
RBTree()
:root(nullptr)
{}
//插入结点
bool Insert(const pair<k, v>& kv) {
if (root == nullptr) {
root = new Node(kv);
root->color = BLACK;
return true;
}
Node* parent = nullptr;
Node* cur = root;
int res;
while (cur) {
res = this->compareTo(kv, cur);
if (res == 1) {
parent = cur;
cur = cur->right;
}
else if (res == -1) {
parent = cur;
cur = cur->left;
}
else {
return false;
}
}
cur = new Node(kv);
cur->color = RED;//新增节点
res = this->compareTo(kv, parent);
//如果比较最终落在右子树,则直接将父节点右指针指向cur
if (res == 1) {
parent->right = cur;
cur->parent = parent;
}
//如果比较最终落在左子树,则直接将父节点左指针指向cur
else {
parent->left = cur;
cur->parent = parent;
}
//调整
fixAfterPut(parent, cur);
root->color = BLACK;
return true;
}
void fixAfterPut(Node* p, Node* cur) {
while (p && p->color == RED) {
Node* grandfather = p->parent;
if (p == grandfather->left) {
Node* uncle = grandfather->right;
//1、uncle存在且为红
if (uncle && uncle->color == RED) {
/* 变色 + 继续向上处理 */
//①变色
p->color = uncle->color = BLACK;
grandfather->color = RED;
//②继续向上处理
cur = grandfather;
p = cur->parent;
}
else {//2、uncle不存在/存在且为黑
if (cur == p->left) {
//单旋
rightRotate(grandfather);
p->color = BLACK;
grandfather->color = RED;
}
else {
//双旋
leftRotate(p);
rightRotate(grandfather);
cur->color = BLACK;
grandfather->color = RED;
}
break;
}
}
else {
Node* uncle = grandfather->left;
//1、uncle存在且为红
if (uncle && uncle->color == RED) {
/* 变色 + 继续向上处理 */
//①变色
p->color = uncle->color = BLACK;
grandfather->color = RED;
//②继续向上处理
cur = grandfather;
p = cur->parent;
}
else {//2、uncle不存在/存在且为黑
if (cur == p->right) {
//单旋
leftRotate(grandfather);
p->color = BLACK;
grandfather->color = RED;
}
else {
//双旋
rightRotate(p);
leftRotate(grandfather);
cur->color = BLACK;
grandfather->color = RED;
}
break;
}
}
}
}
int compareTo(const pair<k, v> kv, Node* cur) {
if (kv.first < cur->kv.first)
return -1;
else if (kv.first > cur->kv.first)
return 1;
else
return 0;
}
//左旋
/*
》》围绕p左旋:
①p是左孩子
pf pf
/ /
p pr(r)
/ \ / \
pl pr(r) ==> p rr(r)
/ \ / \
rl rr pl rl
②p是右孩子
pf pf
\ \
p pr(r)
/ \ / \
pl pr(r) ==> p rr(r)
/ \ / \
rl rr pl rl
*/
void leftRotate(Node* p) {
if (p) {
Node* r = p->right;
Node* rl = r->left;
p->right = rl;
if (rl) {
rl->parent = p;
}
Node* parentParent = p->parent;
//(双向)
r->left = p;//p是r的左孩子
p->parent = r;//r是p的父亲
if (p == root) {//若p是根结点
root = r;
r->parent = nullptr;
}
else {
if (parentParent->left == p) { //p是左孩子
parentParent->left = r;
}
else { //p是右孩子
parentParent->right = r;
}
r->parent = parentParent;
}
}
}
//右旋
/*
》》围绕p右旋:
①p是左孩子
pf pf
/ /
p pl(l)
/ \ ==》 / \
pl(l) pr ll p
/ \ / \
ll lr lr pr
②p是右孩子
pf pf
\ \
p pl(l)
/ \ ==> / \
pl(l) pr ll p
/ \ / \
ll lr lr pr
*/
void rightRotate(Node* p) {
if (p) {
Node* l = p->left;
Node* lr = l->right;
p->left = lr;
if (lr) {
lr->parent = p;
}
Node* parentParent = p->parent;
l->right = p;
p->parent = l;
if (p == root) {
root = l;
root->parent = nullptr;
}
else {
if (parentParent->left == p) {
parentParent->left = l;
}
else {
parentParent->right = l;
}
l->parent = parentParent;
}
}
}
/*
void leftRotate(Node* p) {
if (p != nullptr) {
Node* r = p->right;
p->right = r->left;
if (r->left != nullptr) {
r->left->parent = p;
}
r->parent = p->parent;
if (p->parent == nullptr) {
root = r;
}
else if (p->parent->left == p) {
p->parent->left = r;
}
else {
p->parent->right = r;
}
r->left = p;
p->parent = r;
}
}
void rightRotate(Node* p) {
if (p != nullptr) {
Node* l = p->left;
p->left = l->right;
if (l->right != nullptr) {
l->right->parent = p;
}
l->parent = p->parent;
if (p->parent == nullptr) {
root = l;
}
else if (p->parent->right == p) {
p->parent->right = l;
}
else {
p->parent->left = l;
}
l->right = p;
p->parent = l;
}
}
*/
void InOrder()
{
_InOrder(root);
}
//中序遍历
void _InOrder(Node* root) {
if (root == nullptr) {
return;
}
_InOrder(root->left);
cout << root->kv.first << ":" << root->kv.second << endl;
_InOrder(root->right);
}
/*
检测是否满足红黑树的性质:任意选取一条路径找出黑色结点的个数作为基准值,然后依次与其他
路径比较,如果都相同则满足红黑树的性质。
*/
bool IsBalance() {
if (root && root->color == RED) {
cout << "根结点不是黑色" << endl;//为了测试
return false;
}
//最左路径黑色结点数量做基准值
int banchmark = 0;//保存最左侧路径中黑色结点的个数
Node* left = root;
while (left) {
if (left->color == BLACK)
++banchmark;
left = left->left;
}
int blackNum = 0;//递归遍历时记录黑色结点的个数
return _IsBalance(root, banchmark, blackNum);
}
bool _IsBalance(Node* root, int banchmark, int blackNum)
{
//走到null之后,判断banchmark和blackNum是否相等
if (root == nullptr)
{
if (banchmark != blackNum)
{
cout << "存在路径黑色结点的数量不相等" << endl;//为了测试
return false;
}
return true;
}
if (root->color == RED && root->parent->color == RED)
{
cout << "出现了连续的红色结点" << endl;//为了测试
return false;
}
if (root->color == BLACK)// 统计黑色节点的个数
{
++blackNum;
}
return _IsBalance(root->left, banchmark, blackNum)
&& _IsBalance(root->right, banchmark, blackNum);
}
int Height()
{
return _Height(root);
}
int _Height(Node* root)
{
if (root == nullptr)
{
return 0;
}
int leftHeight = _Height(root->left);
int rightHeight = _Height(root->right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
Node* getRoot() {
return this->root;
}
/*
void predecessor(Node* node)
找到指定结点的前驱结点,即找小于node结点的最大值
*/
Node* predecessor(Node* node) {
if (node == nullptr) {
return nullptr;
}
else if (node->left != nullptr) {
Node* p = node->left;
while (p->right != nullptr) {
p = p->right;
}
return p;
}
else {
Node* p = node->parent;
Node* cur = node;
while (p != nullptr && cur == p->left) {
cur = p;
p = p->parent;
}
return p;
}
}
/*
void successor(Node* node)
找后继结点:即大于结点的最小值
*/
Node* successor(Node* node) {
if (node == nullptr) {
return nullptr;
}
else if (node->right != nullptr) {
Node* p = node->right;
while (p->left != nullptr) {
p = p->left;
}
return p;
}
else {
Node* p = node->parent;
Node* cur = node;
while (p != nullptr && cur == p->right) {
cur = p;
p = p->parent;
}
return p;
}
}
Node* getNode(const pair<k, v> kv) {
Node* node = this->root;
while (node != nullptr) {
//int cmp = key.compareTo((K)node.key);
int cmp = compareTo(kv, node);
if (cmp < 0) {
node = node->left;
}
else if (cmp > 0) {
node = node->right;
}
else
return node;
}
return nullptr;
}
v remove(const pair<k, v> kv) {
Node* node = getNode(kv);
if (node == nullptr) {
return "";
}
//V oldValue = (V)node.value;
v oldValue = kv.second;
deleteNode(node);
return oldValue;
}
/**
* 删除操作:
* 1、删除叶子结点,直接删除
* 2、删除的结点有一个子结点,那么用子结点来替代
* 3、如果删除的节点有2个子结点,此时需要找到前驱结点或者后继结点来替代
*
*/
void deleteNode(Node* node) {
//3、node结点有2个孩子【即node是度为2的结点】
if (node->left != nullptr && node->right != nullptr) {
//后继结点替代
Node* replaceNode = successor(node);
node->kv.first = replaceNode->kv.first;
node->kv.second = replaceNode->kv.second;
node = replaceNode;
}
//node的孩子结点是度为0或1的结点
Node* replacement = node->left != nullptr ? node->left : node->right;
//2、替代结点不为空
if (replacement != nullptr) {
//替代者的父指针指向的原来node的父亲
replacement->parent = node->parent;
//node是根节点
if (node->parent == nullptr) {
root = replacement;
}
//node是左孩子,所以替代者依然是左孩子
else if (node == node->parent->left) {
node->parent->left = replacement;
}
//node是右孩子,所以替代者依然是右孩子
else {
node->parent->right = replacement;
}
//将node的左右孩子指针和父指针都指向null(此时node处于游离状态,等待垃圾回收)
node->left = node->right = node->parent = nullptr;
//替换完之后需要调整平衡
if (node->color == BLACK) {
//需要调整,这种情况一定是红色(替代节点一定是红色,此时只要变色)
fixAfterRemove(replacement);
}
delete node;
}
//删除结点就是根结点
else if (node->parent == nullptr) {
root = nullptr;
}
//1、node结点是叶子结点,replacement为nullptr
else {
//先调整
if (node->color == BLACK) {
fixAfterRemove(node);
}
//再删除
if (node->parent != nullptr) {
if (node == node->parent->left) {
node->parent->left = nullptr;
}
else if (node == node->parent->right) {
node->parent->right = nullptr;
}
node->parent = nullptr;
delete node;
}
}
}
Colour colorOf(Node* node) {
return node == nullptr ? BLACK : node->color;
}
/**
* 删除后调整
*/
void fixAfterRemove(Node* x) {
while (x != root && x->color == BLACK) {
//x是左孩子的情况
if (x == x->parent->left) {
//兄弟节点
Node* rnode = x->parent->right;
//判断此时兄弟节点是否是真正的兄弟节点
if (rnode->color == RED) {
rnode->color = BLACK;
x->parent->color = RED;
leftRotate(x->parent);
//找到真正的兄弟节点
rnode = x->parent->right;
cout << "找到真正的兄弟节点:"<<rnode->kv.first << endl;
}
//情况三,找兄弟借,兄弟没得借
//if (rnode->left == nullptr && rnode->right == nullptr || rnode->left->color==BLACK && rnode->right->color == BLACK) {
if (colorOf(rnode->left) == BLACK && colorOf(rnode->right) == BLACK){
//情况复杂,暂时不写
rnode->color = RED;
x = x->parent;
}
//情况二,找兄弟借,兄弟有的借
else {
//分2种小情况:兄弟节点本来是3节点或者是4节点的情况
if (colorOf(rnode->right) == BLACK) {
//if (rnode->right == nullptr) {
rnode->left->color = BLACK;
rnode->color = RED;
rightRotate(rnode);
rnode = x->parent->right;
}
rnode->color = x->parent->color;
x->parent->color = BLACK;
if(rnode->right)
rnode->right->color = BLACK;
leftRotate(x->parent);
x = root;
}
}
//x是右孩子的情况
//x是右孩子的情况
else {
//兄弟节点
Node* lnode = x->parent->left;
//判断此时兄弟节点是否是真正的兄弟节点
if (lnode->color == RED) {
lnode->color = BLACK;
x->parent->color = RED;
rightRotate(x->parent);
//找到真正的兄弟节点
lnode = x->parent->left;
}
//情况三,找兄弟借,兄弟没得借
//if (lnode->right == nullptr && lnode->left == nullptr || lnode->left->color == BLACK && lnode->right->color == BLACK)
if(colorOf(lnode->left) == BLACK && colorOf(lnode->right) == BLACK){
//情况复杂,暂时不写
lnode->color = RED;
x = x->parent;
}
//情况二,找兄弟借,兄弟有的借
else {
//分2种小情况:兄弟节点本来是3节点或者是4节点的情况
if (colorOf(lnode->left) == BLACK) {
//if (lnode->left == nullptr) {
lnode->right->color = BLACK;
lnode->color = RED;
leftRotate(lnode);
lnode = x->parent->left;
}
lnode->color = x->parent->color;
x->parent->color = BLACK;
if(lnode->left)
lnode->left->color = BLACK;
rightRotate(x->parent);
x = root;
}
}
}
//情况一、替代节点是红色,则直接染黑,补偿删除的黑色节点,这样红黑树依然保持平衡
x->color = BLACK;
}
private:
Node* root;
};
template<class k, class v>
class TreeOperation {
typedef RBTreeNode<k, v> Node;
/*
树的结构示例:
1
/ \
2 3
/ \ / \
4 5 6 7
*/
// 用于获得树的层数
public:
int getTreeDepth(Node* root) {
if (root == nullptr)
return 0;
else
{
int ldep = getTreeDepth(root->left);
int rdep = getTreeDepth(root->right);
return (ldep < rdep) ? rdep + 1 : ldep + 1;
}
}
void writeArray(Node* currNode, int rowIndex, int columnIndex, string** res, int treeDepth) {
// 保证输入的树不为空
if (currNode == nullptr) return;
// 0、默认无色
//res[rowIndex][columnIndex] = String.valueOf(currNode.getValue());
//1、颜色表示
if (currNode->color == BLACK) {//黑色,加色后错位比较明显
res[rowIndex][columnIndex] = ("\033[40;3m" + currNode->kv.second + "\033[0m");
//cout << "-----------------------------------------" << res[rowIndex][columnIndex] << endl;
}
else {
res[rowIndex][columnIndex] = ("\033[31;3m" + currNode->kv.second + "\033[0m");
//cout << "=========================================" << res[rowIndex][columnIndex] << endl;
}
//2、R,B表示
//res[rowIndex][columnIndex] = String.valueOf(currNode.getValue()+"-"+(currNode.isColor()?"B":"R")+"");
// 计算当前位于树的第几层
int currLevel = ((rowIndex + 1) / 2);
// 若到了最后一层,则返回
if (currLevel == treeDepth) return;
// 计算当前行到下一行,每个元素之间的间隔(下一行的列索引与当前元素的列索引之间的间隔)
int gap = treeDepth - currLevel - 1;
// 对左儿子进行判断,若有左儿子,则记录相应的"/"与左儿子的值
if (currNode->left != nullptr) {
res[rowIndex + 1][columnIndex - gap] = "/";
writeArray(currNode->left, rowIndex + 2, columnIndex - gap * 2, res, treeDepth);
}
// 对右儿子进行判断,若有右儿子,则记录相应的"\"与右儿子的值
if (currNode->right != nullptr) {
res[rowIndex + 1][columnIndex + gap] = "\\";
writeArray(currNode->right, rowIndex + 2, columnIndex + gap * 2, res, treeDepth);
}
}
void show(Node* root) {
if (root == nullptr) printf("EMPTY!");
//printf("==============");
// 得到树的深度
int treeDepth = getTreeDepth(root);
// 最后一行的宽度为2的(n - 1)次方乘3,再加1
// 作为整个二维数组的宽度
int arrayHeight = treeDepth * 2 - 1;
int arrayWidth = (2 << (treeDepth - 2)) * 3 + 1;
/*printf("==============%d \n", arrayHeight);
printf("==============%d \n", arrayWidth);*/
// 用一个字符串数组来存储每个位置应显示的元素
//string res[arrayHeight][arrayWidth];
int rows = arrayHeight;
int cols = arrayWidth;
//动态创建二维字符数组arr[m][n]
string** res;
res = new string * [arrayHeight]; //创建行指针
for (int i = 0; i < arrayHeight; i++)
res[i] = new string[arrayWidth]; //为每行分配空间
// 对数组进行初始化,默认为一个空格
for (int i = 0; i < arrayHeight; i++)
{
for (int j = 0; j < arrayWidth; j++)
{
res[i][j] = " ";
}
}
// 从根节点开始,递归处理整个树
writeArray(root, 0, arrayWidth / 2, res, treeDepth);
for (int i = 0; i < arrayHeight; i++)
{
vector<string> strVector = {};
for (int j = 0; j < arrayWidth; j++) {
cout << res[i][j];
int tempStrLen = res[i][j].length();
if (tempStrLen > 1 && j <= arrayHeight - 1) {
j += tempStrLen > 4 ? 2 : tempStrLen - 1;
}
//strVector.push_back(res[i][j]);
/*if (res[i][j].length() > 1 && j <= arrayHeight - 1) {
j += res[i][j].length() > 4 ? 2 : res[i][j].length() - 1;
}*/
}
cout << endl;
/*string str="";
for (vector<string>::const_iterator it = strVector.begin(); it != strVector.end(); it++)
{
str.append(*it);
}
cout << str << endl;*/
}
}
};
void insertOpt() {
RBTree<string, string> t;
int inputV = 0;
string str = "";
while (true) {
printf("请输入你要插入的节点:");
scanf_s("%d", &inputV);
str = to_string(inputV);
//这里代码最多支持3位数,3位以上的话红黑树显示太错位了,这里就不重构代码了,大家可自行重构
if (str.length() == 1) {
str = "00" + str;
}
else if (str.length() == 2) {
str = "0" + str;
}
t.Insert(make_pair(str, str));
TreeOperation<string, string> Tp;
Tp.show(t.getRoot());
}
}
void TestRBTree()
{
//RBTree<int, int> t;
RBTree<string, string> t;
//string a[] = { "5","4","3","2","1","0"};
//string a[] = { "16","3","7","11","9","26","18","14","15"};
string a[] = { "4","2","6","1","3","5","15","7","16","14" };
//string a[] = { "4","2","6","1","3","5","15","7","16","14","8","9","10","11","12","13","14"};
//string a[] = { "6","4","10","8","3","7","9"};
for (auto str : a)
{
if (str.length() == 1) {
str = "00" + str;
}
else if (str.length() == 2) {
str = "0" + str;
}
t.Insert(make_pair(str, str));
//cout << "Insert" << e << ":" << t.IsBalance() << endl;
}
/*t.InOrder();
cout << t.IsBalance() << endl;
cout << t.Height() << endl;*/
//TreeOperation<int, int> Tp;
TreeOperation<string, string> Tp;
Tp.show(t.getRoot());
}
void DeleteOpt() {
RBTree<string, string> rbt;
//string a[] = { "6","4","10","8","3","7","9" };
//string a[] = { "16","3","7","11","9","26","18","14","15" };
//string a[] = { "4","2","6","1","3","5","15","7","16","14","8","9","10","11","12","13" };
//string a[] = { "4","2","8","1","3","0","7","13","11","16","10","12","15","17" };
string a[] = { "2","1","3","4","6","8","5","7","9","10" };
for (auto str : a)
{
if (str.length() == 1) {
str = "00" + str;
}
else if (str.length() == 2) {
str = "0" + str;
}
rbt.Insert(make_pair(str, str));
}
TreeOperation<string, string> Tp;
Tp.show(rbt.getRoot());
if (rbt.IsBalance()) {
cout << "RIGHT!!!!这棵红黑树是平衡的" << endl;
cout << endl;
cout << endl;
}
else {
cout << "Error!!!!这棵红黑树是不平衡的" << endl;
cout << endl;
cout << endl;
}
int inputV = 0;
string str = "";
while (true) {
cout << "请输入你要删除的节点:" << endl;
scanf_s("%d", &inputV);
str = to_string(inputV);
//这里代码最多支持3位数,3位以上的话红黑树显示太错位了,这里就不重构代码了,大家可自行重构
if (str.length() == 1) {
str = "00" + str;
}
else if (str.length() == 2) {
str = "0" + str;
}
//1 2 3 88 66 77 100 5 4 101
if (rbt.getRoot()->kv.first == str) {
rbt.remove(make_pair(str, str));
if (rbt.getRoot() == nullptr) {
cout << "根结点已经删除!!!!无结点可删了,退出循环" << endl;
break;
}
Tp.show(rbt.getRoot());
if (rbt.IsBalance()) {
cout << "RIGHT!!!!这棵红黑树是平衡的" << endl;
cout << endl;
cout << endl;
}
else {
cout << "Error!!!!这棵红黑树是不平衡的" << endl;
cout << endl;
cout << endl;
}
}
else {
rbt.remove(make_pair(str, str));
Tp.show(rbt.getRoot());
if (rbt.IsBalance()) {
cout << "RIGHT!!!!这棵红黑树是平衡的" << endl;
cout << endl;
cout << endl;
}
else {
cout << "Error!!!!这棵红黑树是不平衡的" << endl;
cout << endl;
cout << endl;
}
}
}
}
int main() {
/*TestRBTree();
cout << endl;
cout << endl;
cout << endl;*/
//insertOpt();
DeleteOpt();
system("pause");
return 0;
}
演示效果: