//#ifdef __RBTREE_H__
#ifndef __RBTREE_INL_H__
#define __RBTREE_INL_H__
#include "RBTree.h"
#ifdef _RBTREE_DEBUG
#include "../../DebugInfo/Include/DebugInfo.h"
#endif // _RBTREE_DEBUG
template<class KEY, class VALUE>
RBTREE::RBTree<KEY, VALUE>::RBTree() :m_pRoot(nullptr), m_pHead(nullptr),m_pTail(nullptr)
{
#ifdef _RBTREE_DEBUG
Info("构造红黑树:{}", fmt::ptr(this));
#endif
}
template<class KEY, class VALUE>
RBTREE::RBTree<KEY, VALUE>::~RBTree()
{
#ifdef _RBTREE_DEBUG
Info("析构红黑树:{}", fmt::ptr(this));
#endif
//Destroy();
ListDestroy();
}
template<class KEY, class VALUE>
RBTREE::RBTree<KEY, VALUE>::RBTree(RBTree&& _Instance) :
m_pRoot(_Instance.m_pRoot),
m_pHead(_Instance.m_pHead),
m_pTail(_Instance.m_pTail)
{
#ifdef _RBTREE_DEBUG
Info("移动构造红黑树:{}", fmt::ptr(this));
#endif
_Instance.m_pRoot = nullptr;
_Instance.m_pHead = nullptr;
_Instance.m_pTail = nullptr;
}
/************************************************************************************************************************************************/
//向红黑树插入元素
template<class KEY, class VALUE>
VALUE* RBTREE::RBTree<KEY, VALUE>::Insert(KEY&& _Instance, VALUE&& _Value)
{
#ifdef _RBTREE_DEBUG
Info("------------------------------------------------------------------------------------------------------------");
#endif
RBTreeNode<KEY,VALUE>* pNewNode = new RBTreeNode<KEY,VALUE>(std::forward<KEY>(_Instance), std::forward<VALUE>(_Value));
if (!insert(m_pRoot, pNewNode))
{
#ifdef _RBTREE_DEBUG
Warn("红黑树<{}>插入元素 {:d} 失败", fmt::ptr(this), _Instance);
#endif
delete pNewNode;
return nullptr;
}
else
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>插入元素 {:d} 完成", fmt::ptr(this), _Instance);
#endif
//list链表插入节点
if (!m_pHead)
{
m_pHead = pNewNode;
m_pTail = pNewNode;
}
else
{
m_pTail->m_pNext = pNewNode;
pNewNode->m_pFront = m_pTail;
m_pTail = pNewNode;
}
}
return &pNewNode->m_Value;
}
/*
将一个节点插入到红黑树中,需要执行:
第一步: 将红黑树当作一颗二叉查找树,将节点插入。
红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。
此外,无论是左旋还是右旋,若旋转之前这棵树是二叉查找树,旋转之后它一定还是二叉查找树。
这也就意味着,任何的旋转和重新着色操作,都不会改变它仍然是一颗二叉查找树的事实。
好吧?那接下来,我们就来想方设法的旋转以及重新着色,使这颗树重新成为红黑树!
第二步:将插入的节点着色为"红色"。
为什么着色成红色,而不是黑色呢?为什么呢?在回答之前,我们需要重新温习一下红黑树的特性:
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色。 [注意:这里叶子节点,是指为空的叶子节点!]
(4) 如果一个节点是红色的,则它的子节点必须是黑色的。
(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
将插入的节点着色为红色,不会违背"特性(5)"!少违背一条特性,就意味着我们需要处理的情况越少。
接下来,就要努力的让这棵树满足其它性质即可;满足了的话,它就又是一颗红黑树了。o(∩∩)o...哈哈
*/
template<class KEY, class VALUE>
bool RBTREE::RBTree<KEY, VALUE>::insert(RBTreeNode<KEY,VALUE>*& _pRoot, RBTreeNode<KEY,VALUE>* _pTargetNode)
{
// 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中。
RBTreeNode<KEY,VALUE>* pCursor = _pRoot;//操作游标,一般指当前节点
RBTreeNode<KEY,VALUE>* pPreNode = nullptr;//用作 备份上一个节点.当游标指向空节点时,此指针就能直接向上回溯出父节点
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>准备插入元素{:d}", fmt::ptr(this), _pTargetNode->m_Key);
#endif
while (pCursor != nullptr)
{
pPreNode = pCursor;//备份上一个节点,游标将指向下个节点
if (_pTargetNode->m_Key < pCursor->m_Key)
{//目标节点 小于 当前的游标节点
pCursor = pCursor->m_pLeft;//把左孩子赋值给游标
continue;
}
else if (_pTargetNode->m_Key > pCursor->m_Key)
{//目标节点 大于 当前游标节点
pCursor = pCursor->m_pRight;//把右孩子赋值给游标
continue;
}
else
{
//游标节点 等于 目标节点
//退出循环,因为插入操作 找到了相同节点,直接返回false
#ifdef _RBTREE_DEBUG
Warn("红黑树<{}>插入元素{:d}时候找到相同的值,插入失败", fmt::ptr(this), _pTargetNode->m_Key);
#endif
return false;
}
}
//代码执行到此,说明游标节点已经指向到叶子节点(空节点)
pCursor = pPreNode;//游标操作节点为空,把备份节点恢复回到游标操作节点. 纯属增强可读性
//代码执行到此,说明游标节点不为空,并且是最后一个有效的尾节点(非空节点)
//代码执行到此,说明游标节点的 左右孩子 其中一个肯定是叶子节点(空节点)
//开始建立 目标节点与当前游标点 之间的链接
//新插入的节点父节点就是当前游标节点
_pTargetNode->m_pParent = pCursor;//定义目标节点的父节点
//准备定义游标操作节点的 左右孩子
if (pCursor != nullptr)
{
if (_pTargetNode->m_Key < pCursor->m_Key)
{//目标节点 小于 当前游标操作节点
//执行到此,说明游标操作节点的 左孩子 肯定是空节点了
//开始对红黑树插入新节点, 插入当前游标操作节点左边
pCursor->m_pLeft = _pTargetNode;//游标节点的左孩子 = 目标节点
_pTargetNode->m_Fork = Fork::LEFT;//插入的新节点属于左孩子
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>插入元素{:d}完成, 是父节点<{:d}>的左孩子", fmt::ptr(this), _pTargetNode->m_Key, pCursor->m_Key);
#endif
}
else if (_pTargetNode->m_Key > pCursor->m_Key)
{//目标节点 大于 当前游标操作节点
//执行到此,说明游标操作节点的 右孩子 肯定是空节点了
//开始对红黑树插入新节点, 插入当前游标操作节点右边
pCursor->m_pRight = _pTargetNode;//游标节点的右孩子 = 目标节点
_pTargetNode->m_Fork = Fork::RIGHT;//插入的新节点属于右孩子
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>插入元素{:d}完成, 是父节点<{:d}>的右孩子", fmt::ptr(this), _pTargetNode->m_Key, pCursor->m_Key);
#endif
}
else
{
#ifdef _RBTREE_DEBUG
assert(false);//上面已经返回false,不可能执行到此
#endif
//插入的目标节点 与 游标节点 相等。需要特殊处理
return false;
}
}
else
{
//如果连备份节点都没有备份过,为空。即root节点肯定是空
//直接定义root节点为插入的目标节点
_pRoot = _pTargetNode;//此时的&_pRoot指针地址引用就发挥作用了
_pRoot->m_Color = Color::BLACK;//根节点必须是黑色
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>插入元素{:d}完成, 元素是根节点", fmt::ptr(this), _pTargetNode->m_Key);
#endif
return true;
}
//设置插入目标节点的颜色为: 红色. 插入新节点为红色对红黑树的性质5影响最小
// _pTargetNode->m_Color = Color::RED;//节点初始化已经是红色,无须重复设置
//以上插入节点的代码过程和就是二叉树的插入过程,一模一样的。
//不一样的是以下insert_fix函数重新把二叉树修正成一棵平衡二叉树,通过节点的颜色修正。
//插入新节点后, 重新修正为一颗平衡二叉树
insert_fix(_pRoot, _pTargetNode);
//修正完成后就是一棵标准的: 红黑树
return true;
}
/*
* 红黑树插入修正函数
*
* 在向红黑树中插入节点之后(失去平衡),再调用该函数;
* 目的是将它重新塑造成一颗红黑树。
*
* 参数说明:
* _pRoot 红黑树的根节点
* _pTargetNode 插入的目标节点
*/
/*
修正原理
回忆下红黑树的性质:
性质:
1.每个节点要么是红色, 要么就是黑色。
2.根节点必为黑色。
3.叶子节点(NIL)都为黑色,且为null。
4.红色节点的两个子节点都为黑色(红黑树不会出现相邻的红色节点)(即父与子节点不可能都是红色)
5.从任意节点出发,到其每个叶子节点(NIL)的路径中包含相同数量的黑色节点
衍生特性:
1.新加入到红黑树的节点为红色节点,如果新加入节点是黑色,会影响性质4。所以新节点是红色影响最小
2.根据性质3判断: 叶子节点(NIL)虽然是null, 但也算是一个有效的颜色节点
3.根据性质4判断: 黑色节点的2个子节点可以为2个红色节点。也可以是2个黑色节点,但必须是叶子节点(NIL)(左右树不可能存在有效的黑色节点)
4.根据性质4判断: 黑色节点的2个子点也可以是1个红色节点,一个黑色节点(叶子节点NIL)(左右树不可能存在有效的黑色节点)
5.根据性质5判断: 确保没有一条路径会比其他路径长出俩倍
**************************************************************************
总结插入新节点可能出现的情景,并给出处理方案:
情景1: 插入的节点的父节点是黑色
情景1处理方案:无需任何处理
情景2:插入的节点的父节点是红色(父子节点均为红色,需修正)
根据叔叔节点去拆分3种分支情景:
情景2.1: 叔叔节点是存在,并且是红色
情景2.1处理方案:
将父节点设置成黑色;
将叔叔节点设置成黑色;
将祖父节点设置成红色;
将祖父节点设置成当前节点,继续while循环向上修正
情景2.2: 叔叔节点不存在或者叔叔节点是黑色, 并且插入的节点的父节点是属于其祖父节点的左孩子(叔叔是右孩子)
根据情景2.2去拆分2种分支情景:
情景2.2.1: 插入节点是其父节点的右孩子(内侧插入)
情景2.2.1处理方案:
将当前节点的父节点左旋操作后得到情景2.2.2
处理情景2.2.2
情景2.2.2: 插入节点是其父节点的左孩子(外侧插入)
情景2.2.2处理方案:
将父亲节点设置为黑色
将祖父节点设置成红色
将祖父节点进行右旋操作
情景2.3: 叔叔节点不存在或者叔叔节点是黑色, 并且插入节点的父节点是属于其祖父节点的右孩子(叔叔是左孩子)
根据情景2.3去拆分2种分支情景:
情景2.3.1: 插入的节点是其父节点的左孩子(外侧插入)
情景2.3.1处理方案:
将当前节点的父节点右旋操作后得到情景2.3.2
处理情景2.3.2
情景2.3.2: 插入的节点是其父节点的右孩子(内侧插入)
情景2.3.2处理方案:
将父新节点设置成黑色
将祖父节点设置成红色
将祖父节点进行左旋操作
*/
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::insert_fix(RBTreeNode<KEY,VALUE>*& _pRoot, RBTreeNode<KEY,VALUE>* _pTargetNode)
{//隐藏条件: 新插入的节点的左右2个叶子(NIL),肯定为空,并且是黑色节点
RBTreeNode<KEY,VALUE>* pCursor = _pTargetNode;//当前的操作游标节点
RBTreeNode<KEY,VALUE>* pGParent = nullptr;//用作储存祖父节点
RBTreeNode<KEY,VALUE>* pParent = nullptr;//用作储存父类节点
RBTreeNode<KEY,VALUE>* pUncle = nullptr;//用作储存叔叔节点
RBTreeNode<KEY,VALUE>* pTmp = nullptr;//临时备份节点,用作2点交换
/********************************************************************/
//情景1: 插入的节点的父节点是黑色
//情景1处理方案 : 无需任何处理
/********************************************************************/
//情景2: 插入节点的父节点是红色
while ((pParent = pCursor->m_pParent) && pParent->m_Color == Color::RED)
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>插入元素{:d}完成, 需要修正平衡(原因是父节点<{:d}>是红色)", fmt::ptr(this), _pTargetNode->m_Key, pParent->m_Key);
#endif
pGParent = pParent->m_pParent;//隐藏条件,插入节点的父节点是红色,那么祖父节点必然存在,并且是黑色
//通过父节点找出叔叔节点
pUncle = pParent->m_Fork == Fork::LEFT ? pGParent->m_pRight : pParent->m_Fork == Fork::RIGHT ? pGParent->m_pLeft : nullptr;
//通过叔叔节点再分拆开3个情景:
//情景2.1: 叔叔节点存在,并且为红色
//情景2.2: 叔叔节点不存在或是黑色节点并且父节点是祖父节点的左孩子(叔叔是右孩子)
//情景2.3: 叔叔节点不存在或是黑色节点并且父节点是祖父节点的右孩子(叔叔是左孩子)
if (pUncle && pUncle->m_Color == Color::RED)//情景2.1: 叔叔节点存在,并且为红色
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>插入元素{:d}修正平衡,父节点<{:d}>是红色, 叔叔节点<{:d}>存在也是红色,触发情景2.1(颜色持衡)", fmt::ptr(this), _pTargetNode->m_Key, pParent->m_Key, pUncle->m_Key);
#endif
//情景2.1处理方案:
//将父节点设置成黑色;
//将叔叔节点设置成黑色;
//将祖父节点设置成红色;
//将祖父节点设置成当前节点, 继续while循环向上修正
pParent->m_Color = Color::BLACK;
pUncle->m_Color = Color::BLACK;
pGParent->m_Color = Color::RED;
pCursor = pGParent;
continue;
}
else
{
if (pParent->m_Fork == Fork::LEFT) //情景2.2: 叔叔节点不存在或者叔叔节点是黑色, 并且插入的节点的父节点是属于其祖父节点的左孩子(叔叔是右孩子)
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>插入元素{:d}修正平衡,父节点<{:d}>是红色, 叔叔节点不存在或是黑色并是右孩子,触发情景2.2", fmt::ptr(this), _pTargetNode->m_Key, pParent->m_Key);
#endif
//根据情景2.2去拆分2种分支情景:
//情景2.2.1 : 插入节点是其父节点的右孩子(内侧插入)
//情景2.2.1处理方案 :
//将当前节点的父节点左旋操作后得到情景2.2.2
//处理情景2.2.2
//情景2.2.1: 插入节点是其父节点的右孩子
if (pCursor->m_Fork == Fork::RIGHT)
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>插入元素{:d}修正平衡触发情景2.2.1,准备左旋", fmt::ptr(this), _pTargetNode->m_Key);
#endif
left_rotate(_pRoot, pParent);//父节点左旋操作, 左旋完成后,已经得到情景2.2.2
//把当前节点和父节点的操作指针交换,为下面情景2.2.2的处理方案设置颜色作准备
//2点交换开始
pTmp = pCursor;
pCursor = pParent;
pParent = pTmp;
//已经得到情景2.2.2,往下代码开始处理2.2.2
}
//情景2.2.2 : 插入节点是其父节点的左孩子(外侧插入)
//情景2.2.2处理方案 :
//将父亲节点设置为黑色
//将祖父节点设置成红色
//将 祖父节点 进行右旋操作
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>插入元素{:d}修正平衡触发情景2.2.2,准备右旋", fmt::ptr(this), _pTargetNode->m_Key);
#endif
pParent->m_Color = Color::BLACK;
pGParent->m_Color = Color::RED;
right_rotate(_pRoot, pGParent);
}
else if (pParent->m_Fork == Fork::RIGHT) //情景2.3: 叔叔节点不存在或者叔叔节点是黑色, 并且插入节点的父节点是属于其祖父节点的右孩子(叔叔是左孩子)
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>插入元素{:d}修正平衡,父节点<{:d}>是红色, 叔叔节点不存在或是黑色并是左孩子,触发情景2.3", fmt::ptr(this), _pTargetNode->m_Key, pParent->m_Key);
#endif
//根据情景2.3去拆分2种分支情景 :
//情景2.3.1 : 插入的节点是其父节点的左孩子(外侧插入)
//情景2.3.1处理方案 :
//将当前节点的父节点右旋操作后得到情景2.3.2
//处理情景2.3.2
//情景2.3.1: 插入的节点是其父节点的左孩子
if (pCursor->m_Fork == Fork::LEFT)
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>插入元素{:d}修正平衡触发情景2.3.1,准备右旋", fmt::ptr(this), _pTargetNode->m_Key);
#endif
right_rotate(_pRoot, pParent);//父节点右旋操作, 右旋完成后已经得到情景2.3.2
//把当前节点和父节点的操作指针交换,为下面情景2.3.2的处理方案设置颜色作准备
//2点交换开始
pTmp = pCursor;
pCursor = pParent;
pParent = pTmp;
//已经得到情景2.3.2,往下代码开始处理情景2.3.2
}
//情景2.3.2 : 插入的节点是其父节点的右孩子(内侧插入)
//情景2.3.2处理方案 :
//将父亲节点设置成黑色
//将祖父节点设置成红色
//将 祖父节点 进行左旋操作
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>插入元素{:d}修正平衡触发情景2.3.2,准备左旋", fmt::ptr(this), _pTargetNode->m_Key);
#endif
pParent->m_Color = Color::BLACK;
pGParent->m_Color = Color::RED;
left_rotate(_pRoot, pGParent);
}
}
}
/********************************************************************/
_pRoot->m_Color = Color::BLACK;//无条件将根节点设置成黑色
}
/************************************************************************************************************************************************/
/*
* 对红黑树的节点(x)进行左旋转
*
* 左旋示意图(对节点x进行左旋):
* px px
* / /
* x y
* / \ --(x左旋)--> / \
* lx y x ry
* / \ / \
* ly ry lx ly
*
*
*/
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::left_rotate(RBTreeNode<KEY,VALUE>*& _pRoot, RBTreeNode<KEY,VALUE>* _pTargetNode)
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>插入元素{:d}修正平衡触发左旋", fmt::ptr(this), _pTargetNode->m_Key);
#endif
RBTreeNode<KEY,VALUE>* pParent = _pTargetNode->m_pParent;//取出目标节点的父节点
Fork fFork = _pTargetNode->m_Fork;//备份左旋目标的方位
RBTreeNode<KEY,VALUE>* pRotateResult = _left_rotate(_pTargetNode);
if (pRotateResult == _pTargetNode)
{
//左旋失败,原因左旋目标的右孩子为空
return;
}
switch (fFork)
{
case Fork::LEFT://如果左旋目标是其父节点的左孩子,进行px与y的链接
pParent->m_pLeft = pRotateResult;//设置父节点的左孩子为左旋结果y
pRotateResult->m_pParent = pParent;//设置左旋结果的父节点为 左旋目标的父节点px
pRotateResult->m_Fork = Fork::LEFT;//设置左旋结果的方位为: 左
break;
case Fork::RIGHT://如果左旋目标是其父节点的右孩子,进行px与y的链接
pParent->m_pRight = pRotateResult;//设置父节点的右孩子为左旋结果y
pRotateResult->m_pParent = pParent;//设置左旋结果y的父节点为 左旋目标的父节点px
pRotateResult->m_Fork = Fork::RIGHT;//设置左旋结果的方位为: 右
break;
case Fork::ROOT://如果左旋目标本身就是根节点root,把左旋结果设置成根节点即可
pRotateResult->m_pParent = nullptr;//把左旋结果y的父节点设置为空
pRotateResult->m_Fork = Fork::ROOT;//设置左旋结果的方位为root
_pRoot = pRotateResult;//重新设置根节点为左旋结果y
break;
}
}
/*
* 对红黑树的节点(x)进行右旋转
*
* 右旋示意图(对节点x进行右旋):
* px px
* / /
* x y
* / \ --(x右旋)--> / \
* y rx ly x
* / \ / \
* ly ry ry rx
*
*/
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::right_rotate(RBTreeNode<KEY,VALUE>*& _pRoot, RBTreeNode<KEY,VALUE>* _pTargetNode)
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>插入元素{:d}修正平衡触发右旋", fmt::ptr(this), _pTargetNode->m_Key);
#endif
RBTreeNode<KEY,VALUE>* pParent = _pTargetNode->m_pParent;//取出右旋目标节点的父节点px
Fork fFork = _pTargetNode->m_Fork;//备份右旋目标的方位
RBTreeNode<KEY,VALUE>* pRotateResult = _right_rotate(_pTargetNode);//对x进行右旋并返右旋回结
if (pRotateResult == _pTargetNode)
{
//右旋失败,原因右旋目标的左孩子为空
return;
}
switch (fFork)
{
case Fork::LEFT://如果右旋目标是其父节点的左孩子,进行px与y的链接
pParent->m_pLeft = pRotateResult;//设置父节点的左孩子为右旋结果y
pRotateResult->m_pParent = pParent;//设置右旋结果的父节点为 右旋目标的父节点px
pRotateResult->m_Fork = Fork::LEFT;//设置右旋结果的方位为: 左
break;
case Fork::RIGHT://如果右旋目标是其父节点的右孩子,进行px与y的链接
pParent->m_pRight = pRotateResult;//设置父节点的右孩子为右旋结果y
pRotateResult->m_pParent = pParent;//设置右旋结果y的父节点为 右旋目标的父节点px
pRotateResult->m_Fork = Fork::RIGHT;//设置右旋结果的方位为: 右
break;
case Fork::ROOT://如果右旋目标本身就是根节点root,把右旋结果设置成根节点即可
pRotateResult->m_pParent = nullptr;//把右旋结果y的父节点设置为空
pRotateResult->m_Fork = Fork::ROOT;//设置右旋结果的方位为root
_pRoot = pRotateResult;//重新设置根节点为右旋结果y
break;
}
}
/*
* 对红黑树的节点(x)进行左旋转
*
* 左旋示意图(对节点x进行左旋):
*
* x y
* / \ --(x左旋)--> / \
* lx y x ry
* / \ / \
* ly ry lx ly
*
*
*/
template<class KEY, class VALUE>
RBTREE::RBTreeNode<KEY,VALUE>* RBTREE::RBTree<KEY, VALUE>::_left_rotate(RBTreeNode<KEY,VALUE>* _pTargetNode)
{
RBTreeNode<KEY,VALUE>* pRight = _pTargetNode->m_pRight;//获取右孩子y
RBTreeNode<KEY,VALUE>* pRLeft = nullptr;//用作存储右孩子的左孩子ly
if (!pRight) return _pTargetNode;//如果右孩子为空,还谈什么左旋。根本左旋不了,直接返回左旋目标
pRLeft = pRight->m_pLeft;//获取右孩子的左孩子ly
//准备x与ly进行链接
_pTargetNode->m_pRight = pRLeft;//设置x的右孩子为ly
if (pRLeft)//如果ly不为空
{
pRLeft->m_pParent = _pTargetNode;//设置ly的父节点
pRLeft->m_Fork = Fork::RIGHT;//设置ly的方位
}
//x与ly链接完成
//准备y与x进行链接
pRight->m_pLeft = _pTargetNode;//设置y的左孩子为x
_pTargetNode->m_pParent = pRight;//设置x的父节点为y
_pTargetNode->m_Fork = Fork::LEFT;//设置x的方位
return pRight;//左旋完成, 返回y
}
/*
* 对红黑树的节点(x)进行右旋转
*
* 右旋示意图(对节点x进行右旋):
*
* x y
* / \ --(右旋)--> / \
* y rx ly x
* / \ / \
* ly ry ry rx
*
*/
template<class KEY, class VALUE>
RBTREE::RBTreeNode<KEY,VALUE>* RBTREE::RBTree<KEY, VALUE>::_right_rotate(RBTreeNode<KEY,VALUE>* _pTargetNode)
{
RBTreeNode<KEY,VALUE>* pLeft = _pTargetNode->m_pLeft;//获取左孩子y
RBTreeNode<KEY,VALUE>* pLRight = nullptr;//用作存储左孩子y的右孩子ry
if (!pLeft)return _pTargetNode;//如果左孩子y为空,还谈什么右旋。根本右旋不了,直接返回右旋目标
pLRight = pLeft->m_pRight;//获取左孩子y的右孩子ry
//准备x与ry进行链接
_pTargetNode->m_pLeft = pLRight;//把x的左孩子设置成ry
if (pLRight)
{
pLRight->m_pParent = _pTargetNode;//设置ry的父节点为x
pLRight->m_Fork = Fork::LEFT;//设置ry的方位
}
//x与ry链接完成
//准备y与x进行链接
pLeft->m_pRight = _pTargetNode;//设置y的右孩子为x
_pTargetNode->m_pParent = pLeft;//设置x的父节点为y
_pTargetNode->m_Fork = Fork::RIGHT;//设置x的方位
//y与x的链接完成
return pLeft;//右旋完成, 返回y
}
/************************************************************************************************************************************************/
#ifdef _RBTREE_DEBUG
#ifdef _DEBUG
#ifdef _WIN32
//供打印树使用
#include <stdio.h>
#include <math.h>
#include <Windows.h>
//以树状向控制台打印出红黑树
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::PrintTree()
{
traverse_print(m_pRoot);
}
/*
* 递归打印打印出树形
* KEY 正在打印的树
* depth 目前在打印树的第几层
* right 该子树是否为右子树
* tap 目前子树需要的相对偏移数量
*/
template<class KEY, class VALUE>
int RBTREE::RBTree<KEY, VALUE>::traverse_print(RBTreeNode<KEY,VALUE>* _pRoot, int _iDepth, int _iRight, int _iTap)
{
if (_pRoot == nullptr)return 1;
static int iTreeDepth = tree_depth(_pRoot);
int x, y;
RBTreeNode<KEY,VALUE>* pLeft = nullptr;
RBTreeNode<KEY,VALUE>* pRight = nullptr;
unfold_tree(_pRoot, pLeft, pRight);
int tap1 = _iTap * (int)pow(2, iTreeDepth - _iDepth);
int tap2 = _iRight * (int)pow(2, iTreeDepth - _iDepth);
int tap3 = (int)pow(2, iTreeDepth - _iDepth - 1);
x = tap1 + tap2 + tap3 - 1;
y = _iDepth * 2;
gotoxy(x * 2, y);
printf("%d%c", _pRoot->m_Key, _pRoot->m_Color == Color::RED ? 'r' : _pRoot->m_Color == Color::BLACK ? 'b' : 'N');
_iDepth++;
_iTap = _iTap * 2 + (_iRight == 1 ? 2 : 0);
if (pLeft == nullptr && pRight == nullptr)
{
return 1;
}
else if (pRight == nullptr)
{
gotoxy(x * 2 - tap3, y + 1);
printf("┏");
for (int i = 0; i < tap3 - 1; i++) printf("━");
printf("┛");
traverse_print(pLeft, _iDepth, 0, _iTap);
}
else if (pLeft == NULL)
{
// 打印右子树的位置
gotoxy(x * 2, y + 1);
printf("┗");
for (int i = 0; i < tap3 - 1; i++) printf("━");
printf("┓");
traverse_print(pRight, _iDepth, 1, _iTap);
}
else
{
// 打印左右子树的位置
gotoxy(x * 2 - tap3, y + 1);
printf("┏");
for (int i = 0; i < tap3 - 1; i++) printf("━");
printf("┻");
for (int i = 0; i < tap3 - 1; i++) printf("━");
printf("┓");
traverse_print(pLeft, _iDepth, 0, _iTap);
traverse_print(pRight, _iDepth, 1, _iTap);
}
return 1;
}
// 获取树的深度
template<class KEY, class VALUE>
int RBTREE::RBTree<KEY, VALUE>::tree_depth(RBTreeNode<KEY,VALUE>* _pRoot)
{
if (_pRoot == nullptr) return 0;
int depthLeft, depthRight;
depthLeft = tree_depth(_pRoot->m_pLeft);
depthRight = tree_depth(_pRoot->m_pRight);
return 1 + (depthLeft > depthRight ? depthLeft : depthRight);
}
// 将二叉树分为根,左子树,右子树三个部分
template<class KEY, class VALUE>
int RBTREE::RBTree<KEY, VALUE>::unfold_tree(RBTreeNode<KEY,VALUE>* _pRoot, RBTreeNode<KEY,VALUE>*& _pLeft, RBTreeNode<KEY,VALUE>*& _pRight)
{
if (_pRoot == NULL) return 0;
_pLeft = _pRoot->m_pLeft;
_pRight = _pRoot->m_pRight;
return 1;
}
//改变光标位置
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::gotoxy(int x, int y)
{
// 更新光标位置
COORD pos;
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(hOutput, pos);
}
#endif // _WIN32
#endif //_DEBUG
#endif
template<class KEY, class VALUE>
RBTREE::RBTreeNode<KEY,VALUE>* RBTREE::RBTree<KEY, VALUE>::search(RBTreeNode<KEY,VALUE>* _pRoot, const KEY& _Instance)
{
if (!_pRoot)return nullptr;
if (_Instance == _pRoot->m_Key)return _pRoot;
else if (_Instance < _pRoot->m_Key)return search(_pRoot->m_pLeft, _Instance);
else return search(_pRoot->m_pRight, _Instance);
}
template<class KEY, class VALUE>
bool RBTREE::RBTree<KEY, VALUE>::Remove(const KEY& _Instance)
{
RBTreeNode<KEY,VALUE>* pTarget = search(m_pRoot, _Instance);
if (pTarget)
{
list_remove(pTarget);
remove(m_pRoot, pTarget);
return true;
}
else
{
return false;
}
}
//红黑树的删除节点与二叉树的删除过程一样,只是删除后使用颜色对树的平衡度进行了修正
/*
回顾一下二叉树的节点删除操作,4种情况:
1.没有左右子树
2.只有1个左子树
3.只有1个右子树
4.有2个子树,左右都有
解决方案:
1:没有左右子树
方案1:直接删除,再回马枪告诉父节点(左or右)已经删除,若父节点无,则root为空
2:只有一个左子树
方案2:直接删除,再回马枪告诉父节点(左or右)已经删除,重新链接上删除节点的左子树,并重新设置左子树父节点
或父节点无,则把删除节点的左子树设置成root
3:只有一个右子树
方案3:与方案2逻辑一样。举一反三即可
4:左右子树都存在
方案4:将删除节点的 左子树的最右节点 或 右子树的最左节点替换(左=前继;右=后继)
1.取出右孩子的后继最左节点,与需要删除的目标节点替换。
2.或后继最左节点存在,这个节点必然是最左,不可能存在左孩子。但可能存在右孩子
3.若后继最左节点不存在。则采用右孩子替换
::
*/
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::remove(RBTreeNode<KEY,VALUE>*& _pRoot, RBTreeNode<KEY,VALUE>* _pTargetNode)
{
#ifdef _RBTREE_DEBUG
Info("------------------------------------------------------------------------------------------------------------");
#endif
RBTreeNode<KEY,VALUE>* pParent = _pTargetNode->m_pParent;//目标节点的父节点
RBTreeNode<KEY,VALUE>* pLeft = _pTargetNode->m_pLeft;//目标节点的左节点
RBTreeNode<KEY,VALUE>* pRight = _pTargetNode->m_pRight;//目标节点的右节点
RBTreeNode<KEY,VALUE>* pPrecursor = pLeft;//中序遍历的前继节点(可能是左孩子或者是左孩子的最右节点)
RBTreeNode<KEY,VALUE>* pPreParent = nullptr;//前继节点的父节点
RBTreeNode<KEY,VALUE>* pPreRight = nullptr;//前继节点的右孩子
RBTreeNode<KEY,VALUE>* pPreLeft = nullptr;//前继节点的左孩子
RBTreeNode<KEY,VALUE>* pTmp = nullptr;
Color clrTarget = _pTargetNode->m_Color;//备份删除目标的颜色
Color clrPrecursor = Color::BLACK;//仅初始化前继节点的颜色,具体备份在下面代码过程中备份
Fork forkTarget = _pTargetNode->m_Fork;//备份删除目标位于其父的方位
//1没有左右子树
if (!pLeft && !pRight)
{
if (_pTargetNode->m_Color == Color::RED)
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>删除元素{:d}, 元素信息: 左右孩子为空; 红色", fmt::ptr(this), _pTargetNode->m_Key);
#endif
remove_leaf_red(_pRoot, _pTargetNode);
}
else
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>删除元素{:d}, 元素信息: 左右孩子为空; 黑色", fmt::ptr(this), _pTargetNode->m_Key);
#endif
remove_leaf_black(_pRoot, _pTargetNode);
}
}
else if (pLeft && pRight)//左右子树都存在
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>删除元素{:d}, 元素信息: 左右孩子都存在", fmt::ptr(this), _pTargetNode->m_Key);
#endif
//尝试从左孩子中找出最右前继节点
pPreRight = pPrecursor->m_pRight;//获取前继右节点
while (pPreRight)//若前继节点的最右节点存在
{
pPrecursor = pPreRight;//更新前继节点(最右)
pPreRight = pPrecursor->m_pRight;//继续向右查找最右节点
}
clrPrecursor = pPrecursor->m_Color;//备份前继节点的颜色
//交换颜色,这样就伪装成了,只交换元素,不交换颜色
pPrecursor->m_Color = clrTarget;
_pTargetNode->m_Color = clrPrecursor;
if (pPrecursor != pLeft)//仅有一种情况,找到了前继最右节点
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>删除元素{:d}, 找到了前继最右节点{:d}", fmt::ptr(this), _pTargetNode->m_Key, pPrecursor->m_Key);
#endif
//检测前继最右节点是否拥有左孩子(绝对不可能存在右孩子,否则就不是最右节点了)
pPreParent = pPrecursor->m_pParent;//取出前继节点的父节点
pPreLeft = pPrecursor->m_pLeft;//取出前继节点的左孩子
//把删除目标节点放置到前继节点的位置, 链接操作开始
pPreParent->m_pRight = _pTargetNode;//设置前继点的父节点的右孩子为删除目标节点
_pTargetNode->m_pParent = pPreParent;//回马枪,通知删除目标设置父节点为前继点的父节点
_pTargetNode->m_Fork = Fork::RIGHT;//设置删除目标节点的方位为右孩子
_pTargetNode->m_pRight = nullptr;//重新设置删除目标的右孩子.因为和前继最右是没有右孩子的,不然就不是最右节点了
pPrecursor->m_pLeft = nullptr; // AI关键修复
pPrecursor->m_pRight = nullptr;
if (pPreLeft)//如果前继最右节点存在左孩子
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>删除元素{:d}, 找到了前继最右节点{:d}, 并且存在左孩子{:d}", fmt::ptr(this), _pTargetNode->m_Key, pPrecursor->m_Key, pPreLeft->m_Key);
#endif
_pTargetNode->m_pLeft = pPreLeft;//把删除目标的左孩子设置成前继最右节点的左孩子
pPreLeft->m_pParent = _pTargetNode;//回马枪通知前继最右节点更改父节点为删除目标
}
else//前继最右节点没有左孩子
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>删除元素{:d}, 找到了前继最右节点{:d}, 并且不存在左孩子", fmt::ptr(this), _pTargetNode->m_Key, pPrecursor->m_Key);
#endif
_pTargetNode->m_pLeft = nullptr;
}
//把删除目标节点放置到前继节点的位置, 链接操作完成
//把前继节点放置到删除目标节点的位置, 链接操作开始
switch (forkTarget)
{
case Fork::LEFT:
pParent->m_pLeft = pPrecursor;
pPrecursor->m_Fork = Fork::LEFT;
pPrecursor->m_pParent = pParent;
pPrecursor->m_pLeft = pLeft;
pPrecursor->m_pRight = pRight;
pLeft->m_pParent = pPrecursor;
pRight->m_pParent = pPrecursor;
break;
case Fork::RIGHT:
pParent->m_pRight = pPrecursor;
pPrecursor->m_Fork = Fork::RIGHT;
pPrecursor->m_pParent = pParent;
pPrecursor->m_pLeft = pLeft;
pPrecursor->m_pRight = pRight;
pLeft->m_pParent = pPrecursor;
pRight->m_pParent = pPrecursor;
break;
case Fork::ROOT:
_pRoot = pPrecursor;
pPrecursor->m_Fork = Fork::ROOT;
pPrecursor->m_pParent = pParent;
pPrecursor->m_pLeft = pLeft;
pPrecursor->m_pRight = pRight;
pLeft->m_pParent = pPrecursor;
pRight->m_pParent = pPrecursor;
break;
}
//把前继节点放置到删除目标节点的位置, 链接操作完成
}
else//找不到前继最右节点,即目前的前继节点就是删除目标的左孩子
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>删除元素{:d}, 找不到前继最右节点,前继节点就是其左孩子{:d}", fmt::ptr(this), _pTargetNode->m_Key, pPrecursor->m_Key);
#endif
//把删除目标节点放置到前继节点的位置, 链接操作开始
_pTargetNode->m_pRight = nullptr;//原前继节点没有右孩子
_pTargetNode->m_pLeft = pPreLeft;//设置删除目标的左孩子为前继的左孩子
if (pPreLeft)pPreLeft->m_pParent = _pTargetNode;//如果前继节点的左孩子存在,则设置父节点为删除目标
_pTargetNode->m_pParent = pPrecursor;//把删除目标的父节点设置为前继节点
pPrecursor->m_pLeft = _pTargetNode;//把前继节点的左孩子设置成删除目标
_pTargetNode->m_Fork = Fork::LEFT;//把删除目标的方位设置成左孩子
pPrecursor->m_pRight = pRight;//把前继节点的右孩子设置删除目标的右孩子
pRight->m_pParent = pPrecursor;//把删除目标的右孩子父节点设置成前继节点
//把删除目标节点放置到前继节点的位置, 链接操作完成
//把前继节点放置到删除目标节点的位置, 链接操作开始
switch (forkTarget)
{
case Fork::LEFT:
pParent->m_pLeft = pPrecursor;
pPrecursor->m_pParent = pParent;
pPrecursor->m_Fork = Fork::LEFT;
break;
case Fork::RIGHT:
pParent->m_pRight = pPrecursor;
pPrecursor->m_pParent = pParent;
pPrecursor->m_Fork = Fork::RIGHT;
break;
case Fork::ROOT:
pPrecursor->m_pParent = pParent;
_pRoot = pPrecursor;
pPrecursor->m_Fork = Fork::ROOT;
break;
}
//把前继节点放置到删除目标节点的位置, 链接操作完成
}
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>删除元素{:d}与前继节点{:d}交换完成,准备递归返回执行过程", fmt::ptr(this), _pTargetNode->m_Key, pPrecursor->m_Key);
#endif
remove(_pRoot, _pTargetNode);//删除目标已经退化到达树的末端,重新递归进入删除树节点的过程
}
else if(!pRight && pLeft)//只有一个孩子,左子树
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>删除元素{:d}最有一个左孩子{:d}", fmt::ptr(this), _pTargetNode->m_Key, pLeft->m_Key);
#endif
// 可能会问:
// 左孩子会不会也有左右孩子这类。
// 如果只有左孩子,没有右孩子的话。左孩子是不可能存在孩子了。红黑树不会成立
//删除目标节点与左子树交换,只换元素不换颜色
_pTargetNode->m_pLeft = nullptr;//把目标节点的左孩子设置为空. 因为左子树不存在孩子
_pTargetNode->m_pRight = nullptr;//把目标节点的右孩子设置为空. 因为左子树不存在孩子
_pTargetNode->m_pParent = pLeft;//把目标节点的父节点设置为左子树
_pTargetNode->m_Fork = Fork::LEFT;
pLeft->m_pLeft = _pTargetNode;//把左子树的左孩子设置为删除目标
//pLeft->m_pRight = nullptr;//本来删除目标的右孩子就是空的
pLeft->m_pParent = pParent;
switch (forkTarget)
{
case Fork::LEFT:
pParent->m_pLeft = pLeft;
pLeft->m_Fork = Fork::LEFT;
break;
case Fork::RIGHT:
pParent->m_pRight = pLeft;
pLeft->m_Fork = Fork::RIGHT;
break;
case Fork::ROOT:
_pRoot = pLeft;
pLeft->m_Fork = Fork::ROOT;
break;
}
//删除目标与其左子树的颜色对调, 间接完成了2个节点之间只换元素不换颜色
_pTargetNode->m_Color = pLeft->m_Color;
pLeft->m_Color = clrTarget;
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>删除元素{:d}与左孩子{:d}交换元素完成,准备递归返回执行过程", fmt::ptr(this), _pTargetNode->m_Key, pLeft->m_Key);
#endif
remove(_pRoot, _pTargetNode);//删除目标已经退化到达树的末端,重新递归进入删除树节点的过程
}
else if (pRight && !pLeft)////只有一个孩子,右子树
{
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>删除元素{:d}有一个右孩子{:d}", fmt::ptr(this), _pTargetNode->m_Key, pRight->m_Key);
#endif
// 可能会问:
// 右孩子会不会也有左右孩子这类。
// 如果只有右孩子,没有左孩子的话。右孩子是不可能存在孩子了。红黑树不会成立
//删除目标节点与右子树交换,只换元素不换颜色
_pTargetNode->m_pLeft = nullptr;
_pTargetNode->m_pRight = nullptr;
_pTargetNode->m_pParent = pRight;
_pTargetNode->m_Fork = Fork::RIGHT;
pRight->m_pRight = _pTargetNode;
pRight->m_pParent = pParent;
switch (forkTarget)
{
case Fork::LEFT:
pParent->m_pLeft = pRight;
pRight->m_Fork = Fork::LEFT;
break;
case Fork::RIGHT:
pParent->m_pRight = pRight;
pRight->m_Fork = Fork::RIGHT;
break;
case Fork::ROOT:
_pRoot = pRight;
pRight->m_Fork = Fork::ROOT;
break;
}
//删除目标与其左子树的颜色对调, 间接完成了2个节点之间只换元素不换颜色
_pTargetNode->m_Color = pRight->m_Color;
pRight->m_Color = clrTarget;
#ifdef _RBTREE_DEBUG
Info("红黑树<{}>删除元素{:d}与右孩子{:d}交换元素完成,准备递归返回执行过程", fmt::ptr(this), _pTargetNode->m_Key, pRight->m_Key);
#endif
remove(_pRoot, _pTargetNode);//删除目标已经退化到达树的末端,重新递归进入删除树节点的过程
}
if (m_pRoot) m_pRoot->m_Color = Color::BLACK;//强制设置根节点为黑色,红黑树特性:根必须为黑, // AI关键修复
}
template<class KEY, class VALUE>//删除的叶子节点是红色,只需无条件删除即可
void RBTREE::RBTree<KEY, VALUE>::remove_leaf_red(RBTreeNode<KEY,VALUE>*& _pRoot, RBTreeNode<KEY,VALUE>* _pTargetNode)
{
RBTreeNode<KEY,VALUE>* pParent = _pTargetNode->m_pParent;//目标节点的父节点
Fork fork = _pTargetNode->m_Fork;
delete _pTargetNode;
_pTargetNode = nullptr;
switch (fork)
{
case Fork::LEFT:
pParent->m_pLeft = nullptr;
break;
case Fork::RIGHT:
pParent->m_pRight = nullptr;
break;
case Fork::ROOT:
_pRoot = nullptr;
break;
}
}
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::remove_leaf_black(RBTreeNode<KEY,VALUE>*& _pRoot, RBTreeNode<KEY,VALUE>* _pTargetNode, bool _bIsRemove)
{
RBTreeNode<KEY,VALUE>* pParent = _pTargetNode->m_pParent;//目标节点的父节点
RBTreeNode<KEY,VALUE>* pBrother = nullptr;//目标节点的兄弟节点
RBTreeNode<KEY,VALUE>* pBroLeft = nullptr;////兄弟节点的左孩子, 用作判断(近侄/远侄)节点
RBTreeNode<KEY,VALUE>* pBroRight = nullptr;//兄弟节点的右孩子, 用作判断(近侄/远侄)节点
//如果不是root根节点, 父节点肯定是存在
switch (_pTargetNode->m_Fork)
{
case Fork::LEFT://如果删除目标位于其父的左边****************************************************************
pBrother = pParent->m_pRight;//那么兄弟节点肯定是其父右边
pBroLeft = pBrother ? pBrother->m_pLeft : nullptr;//获取兄弟节点的左孩子(近侄)
pBroRight = pBrother ? pBrother->m_pRight : nullptr;//获取兄弟节点的右孩子(远侄)
//情况1: 兄弟节点为黑色; 父节点红或黑都可以; 远侄子节点为红色; 近侄节点红色或黑色都可,但如是黑色必然是null节点
if (
pBrother && pBrother->m_Color == Color::BLACK && //兄弟节点为黑色
pBroRight && pBroRight->m_Color == Color::RED//远侄子节点为红色,近侄无视(红黑都可以)
)
{
//测试过程:插入<153;100;157;162>删除100触发
//测试过程:插入<153;100;160;157;162;164>删除157触发
remove_leaf_black_case1(_pRoot, pParent, pBrother, pBroRight);
if (_bIsRemove)
{
delete _pTargetNode;
_pTargetNode = nullptr;
pParent->m_pLeft = nullptr;
}
}
//情况2: 兄弟节点B为黑色; 父节点红或黑都可以; 近侄子节点为红色; 远侄子节点为黑色null(不可能为红,如果为红即是情况1了)
else if (
pBrother && pBrother->m_Color == Color::BLACK //兄弟节点为黑色
&& pBroLeft && pBroLeft->m_Color==Color::RED //近侄节点为红色
&& ( (pBroRight&&pBroRight->m_Color==Color::BLACK) || !pBroRight )//远侄必然为黑色
)
{
//测试过程: 插入<153;100;157;155>删除100触发
//测试过程: 插入<153;100;160;157;162;161>删除157
remove_leaf_black_case2(_pRoot, pBrother, pBroLeft);//得到情况1
remove_leaf_black(_pRoot, _pTargetNode, _bIsRemove);//递归进入情况1过程
}
//情况3: 兄弟节点B为黑色; 父亲节P为红色; 兄弟节点的两个孩子(只能是null节点)都为黑色的情况,如果是黑色有效节点,红黑树黑色路径异常
else if (
pBrother && pBrother->m_Color == Color::BLACK
&& pParent->m_Color == Color::RED
&& !pBroLeft && !pBroRight
)
{
//测试过程: 插入<153;100;161;157;162;165>删除165, 之后再删除157触发
remove_leaf_black_case3(pParent, pBrother);
if (_bIsRemove)
{
delete _pTargetNode;
_pTargetNode = nullptr;
pParent->m_pLeft = nullptr;
}
}
//情况4: 兄弟节点B为红色; 父节点P为黑(必然);侄子节点全是黑并非null(必然);必然指红黑树规则已经约束好,无须判断
else if (
pBrother && pBrother->m_Color == Color::RED
&& pParent->m_Color == Color::BLACK
&& pBroLeft && pBroLeft->m_Color == Color::BLACK
&& pBroRight && pBroRight->m_Color == Color::BLACK
)
{
//测试过程: 插入<100;25;153;152;157;162>删除25触发
remove_leaf_black_case4(_pRoot, pParent, pBrother);//得到情况3
remove_leaf_black(_pRoot, _pTargetNode, _bIsRemove);//递归进入情况3过程
}
//情况5: 父亲节点P, 兄弟节点B和兄弟节点的两个孩子(只能为NULL节点)都为黑色的情况
else if (
pParent->m_Color == Color::BLACK
&& pBrother && pBrother->m_Color == Color::BLACK
&& !pBroLeft && !pBroRight
)
{
//测试过程: 插入<100;25;162;10;50;152;175;5;150;157;153;149;151>删除5再删除10触发
//测试过程: 插入<100;25;162;10;50;152;175;5;150;157;153;149;151>删除5再删除50触发
remove_leaf_black_case5(pBrother);
if (_bIsRemove)
{
delete _pTargetNode;
_pTargetNode = nullptr;
pParent->m_pLeft = nullptr;
}
remove_leaf_black(_pRoot, pParent, false);
}
break;
case Fork::RIGHT://如果删除目标位于其父的右边****************************************************************
pBrother = pParent->m_pLeft;//那么兄弟节点肯定是其父左边
pBroLeft = pBrother ? pBrother->m_pLeft : nullptr;//获取兄弟节点的左孩子(远侄)
pBroRight = pBrother ? pBrother->m_pRight : nullptr;//获取兄弟节点的右孩子(近侄)
//情况1: 兄弟节点为黑色; 父节点红或黑都可以; 远侄子节点为红色; 近侄节点红色或黑色都可,但如是黑色必然是null节点
if (
pBrother && pBrother->m_Color == Color::BLACK && //兄弟节点为黑色
pBroLeft && pBroLeft->m_Color == Color::RED//远侄节点为红色,近侄无视(红黑都可以)
)
{
//测试过程:插入<157;153;162;100>删除162触发
//测试过程:插入<170;180;162;160;164;157>删除164触发
remove_leaf_black_case1(_pRoot, pParent, pBrother, pBroLeft);
if (_bIsRemove)
{
delete _pTargetNode;
_pTargetNode = nullptr;
pParent->m_pRight = nullptr;
}
}
//情况2: 兄弟节点B为黑色; 父节点红或黑都可以; 近侄子节点为红色; 远侄子节点为黑色null(不可能为红,如果为红即是情况1了)
else if (
pBrother && pBrother->m_Color == Color::BLACK &&//兄弟节点为黑色
pBroRight && pBroRight->m_Color == Color::RED && //近侄节点为红色
( (pBroLeft && pBroLeft->m_Color == Color::BLACK) || !pBroLeft )//远侄必然为黑色
)
{
//测试过程:插入<157;153;162;154>删除162
//测试过程:插入<170;180;162;160;164;161>删除164
remove_leaf_black_case2(_pRoot, pBrother, pBroRight);//得到情况1
remove_leaf_black(_pRoot, _pTargetNode, _bIsRemove);//递归进入情况1过程
}
//情况3: 兄弟节点B为黑色; 父亲节P为红色; 兄弟节点的两个孩子(只能是null节点)都为黑色的情况,如果是黑色有效节点,红黑树黑色路径异常
else if (
pBrother && pBrother->m_Color == Color::BLACK
&& pParent->m_Color == Color::RED
&& !pBroLeft && !pBroRight
)
{
//测试过程: 插入<153;100;161;157;162;165>删除165, 之后再删除162触发
remove_leaf_black_case3(pParent, pBrother);
if (_bIsRemove)
{
delete _pTargetNode;
_pTargetNode = nullptr;
pParent->m_pRight = nullptr;
}
}
//情况4: 兄弟节点B为红色; 父节点P为黑(必然);侄子节点全是黑并非null(必然);必然指红黑树规则已经约束好,无须判断
else if (
pBrother && pBrother->m_Color == Color::RED
&& pParent->m_Color == Color::BLACK
&& pBroLeft && pBroLeft->m_Color == Color::BLACK
&& pBroRight && pBroRight->m_Color == Color::BLACK
)
{
//测试过程:插入<100;125;53;52;57;45>删除125
remove_leaf_black_case4(_pRoot, pParent, pBrother);//得到情况3
remove_leaf_black(_pRoot, _pTargetNode, _bIsRemove);//递归进入情况3过程
}
//情况5: 父亲节点P, 兄弟节点B和兄弟节点的两个孩子(只能为NULL节点)都为黑色的情况
else if (
pParent->m_Color == Color::BLACK
&& pBrother && pBrother->m_Color == Color::BLACK
&& !pBroLeft && !pBroRight
)
{
//测试过程:插入<100;62;125;110;150;52;75;155;50;57;53;49;51>删除155再删除150触发
//测试过程:插入<100;62;125;110;150;52;75;155;50;57;53;49;51>删除155再删除110触发
remove_leaf_black_case5(pBrother);
if (_bIsRemove)
{
delete _pTargetNode;
_pTargetNode = nullptr;
pParent->m_pRight = nullptr;
}
//remove_leaf_black(_pRoot, pParent, false);
// AI关键修复
while (pParent && pParent->m_Color == Color::BLACK /* 条件 */)
{
remove_leaf_black(_pRoot, pParent, false);
}
}
break;
case Fork::ROOT://****************************************************************
//测试过程: 插入<1> 删除1触发
delete _pTargetNode;
_pTargetNode = nullptr;
_pRoot = nullptr;
break;
}
}
//情况1: 兄弟节点为黑色; 父节点红或黑都可以; 远侄子节点为红色; 近侄节点红色或黑色都可,但近侄如是黑色必然是null节点
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::remove_leaf_black_case1(RBTreeNode<KEY,VALUE>*& _pRoot, RBTreeNode<KEY,VALUE>* _pParent, RBTreeNode<KEY,VALUE>* _pBrother, RBTreeNode<KEY,VALUE>* _pBroChild)
{//调整过程: 将P和B的颜色对调,然后对P树进行(左/右)旋操作,最后把(BR/BL)节点变成黑色,并删除D即可
Color clrParent = _pParent->m_Color;//备份父节点颜色
Color clrBrother = _pBrother->m_Color;//备份兄弟节点颜色
//将P和B节点颜色对调
_pParent->m_Color = clrBrother;
_pBrother->m_Color = clrParent;
switch (_pBrother->m_Fork)
{
case Fork::LEFT://如果兄弟位于其父的左边
right_rotate(_pRoot, _pParent);//将P进行右旋
break;
case Fork::RIGHT://如果兄弟位于其父的右边
left_rotate(_pRoot, _pParent);//将P进行左旋
break;
case Fork::ROOT:
#ifdef _RBTREE_DEBUG
assert(false);//异常,不可能到达
#endif
break;
}
//将远侄节点设置成黑色
_pBroChild->m_Color = Color::BLACK;
}
//情况2: 兄弟节点B为黑色; 父节点红或黑都可以; 近侄子节点为红色; 远侄子节点为黑色null(不可能为红,如果为红即是情况1了)
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::remove_leaf_black_case2(RBTreeNode<KEY,VALUE>*& _pRoot, RBTreeNode<KEY,VALUE>* _pBrother, RBTreeNode<KEY,VALUE>* _pBroChild)
{//调整过程: 将B(左 / 右)旋转, 并将B和(BL / BR)颜色调换, 这时候就得到情况1
Color clrBrother = _pBrother->m_Color;//备份兄弟节点的颜色
Color clrBroChild = _pBroChild->m_Color;//备作近侄节点的颜色
//兄弟节点与近侄节点交换颜色
_pBrother->m_Color = clrBroChild;
_pBroChild->m_Color = clrBrother;
switch (_pBrother->m_Fork)
{
case Fork::LEFT://如果兄弟位于其父的左边
left_rotate(_pRoot, _pBrother);//将B进行左旋
break;
case Fork::RIGHT://如果兄弟位于其父的右边
right_rotate(_pRoot, _pBrother);//将B进行右旋
break;
case Fork::ROOT:
#ifdef _RBTREE_DEBUG
assert(false);//异常,不可能到达
#endif
break;
}
}
//情况3: 兄弟节点B为黑色; 父亲节P为红色; 兄弟节点的两个孩子(只能是null节点)都为黑色的情况, 如果是黑色有效节点, 红黑树黑色路径异常
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::remove_leaf_black_case3(RBTreeNode<KEY,VALUE>* _pParent, RBTreeNode<KEY,VALUE>* _pBrother)
{//调整过程: 将父亲节点P改成黑色,将兄弟节点B改成红色,然后删除D即可
_pParent->m_Color = Color::BLACK;
_pBrother->m_Color = Color::RED;
}
//情况4: 兄弟节点B为红色; 父节点P为黑(必然); 侄子节点全是黑并非null(必然); 必然指红黑树规则已经约束好, 无须判断
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::remove_leaf_black_case4(RBTreeNode<KEY,VALUE>*& _pRoot, RBTreeNode<KEY,VALUE>* _pParent, RBTreeNode<KEY,VALUE>* _pBrother)
{//情况4: 调整过程: 将父亲P和兄弟B的颜色调换, 然后将P进行(左 / 右)旋转操作, 得到情况3
Color clrParent = _pParent->m_Color;//备份父节点颜色
Color clrBrother = _pBrother->m_Color;//备份兄弟节点颜色
//父亲P与兄弟B颜色调换
_pParent->m_Color = clrBrother;
_pBrother->m_Color = clrParent;
switch (_pBrother->m_Fork)
{
case Fork::LEFT://如果兄弟节点位于左边, 删除目标位于右边
right_rotate(_pRoot, _pParent);//将P右旋
break;
case Fork::RIGHT://如果兄弟节点位于右边,删除目标位于左边
left_rotate(_pRoot, _pParent);//将P左旋
break;
case Fork::ROOT:
#ifdef _RBTREE_DEBUG
assert(false);//异常,不可能达到此处
#endif
break;
}
}
//情况5: 父亲节点P, 兄弟节点B和兄弟节点的两个孩子(只能为NULL节点)都为黑色的情况
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::remove_leaf_black_case5(RBTreeNode<KEY,VALUE>* _pBrother)
{
//情况5: 方法是将兄弟节点B的颜色改成红色,这样删除D后P的左右两支的黑节点数就相等了,但是经过P的路径上的黑色节点数会少1.
// 这个时候,我们再以P为起始点,继续根据情况进行平衡操作
//(这句话的意思就是把P当成D(只是不要再删除P了),再看是那种情况,再进行对应的调整。这样一直向上,直到新的起始点为根节点)
_pBrother->m_Color = Color::RED;
}
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::Destroy()
{
if (m_pRoot)destroy(m_pRoot);
m_pHead = nullptr;
m_pTail = nullptr;
}
//查找红黑树是否存在Key
template<class KEY, class VALUE>
VALUE* RBTREE::RBTree<KEY, VALUE>::Find(const KEY& _Instance)
{
RBTreeNode<KEY, VALUE>* pNode = search(m_pRoot, _Instance);
if (pNode)
{
return &pNode->m_Value;
}
return nullptr;
}
//递归清除整棵红黑树
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::destroy(RBTreeNode<KEY,VALUE>* _pTargetNode)
{
if (_pTargetNode->m_pLeft)destroy(_pTargetNode->m_pLeft);
if (_pTargetNode->m_pRight)destroy(_pTargetNode->m_pRight);
if (_pTargetNode)
{
if (_pTargetNode->m_Fork == Fork::ROOT)m_pRoot = nullptr;
delete _pTargetNode;
}
}
template<class KEY, class VALUE>
VALUE& RBTREE::RBTree<KEY, VALUE>::operator[](KEY&& _Instance)
{
RBTreeNode<KEY, VALUE>* pTarget = search(m_pRoot, _Instance);
if (pTarget)
{
return pTarget->m_Value;
}
else
{
return *Insert(std::forward<KEY>(_Instance), VALUE());
}
}
//从链表中删除指定节点,实际不是删除,只是从链表中把指点重新双向链接起来
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::list_remove(RBTreeNode<KEY, VALUE>* _pTargetNode)
{
RBTreeNode<KEY, VALUE>* pFront = _pTargetNode->m_pFront;
RBTreeNode<KEY, VALUE>* pNext = _pTargetNode->m_pNext;
if (!pFront)
{//属于头节点
m_pHead = pNext;
if (pNext)pNext->m_pFront = pFront;
}
else if (_pTargetNode == m_pTail)
{//属于尾节点
if (!pFront)
{
m_pHead = nullptr;
m_pTail = nullptr;
}
else
{
pFront->m_pNext = nullptr;
m_pTail = pFront;
}
}
else
{//属于中间节点
pFront->m_pNext = pNext;
if (pNext)pNext->m_pFront = pFront;
}
}
#ifdef _RBTREE_DEBUG
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::PrintList()
{
RBTreeNode<KEY, VALUE>* pTarget = m_pHead;
while (pTarget)
{
printf("%d\n", pTarget->m_Key);
pTarget = pTarget->m_pNext;
}
}
#endif
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::list_destroy(RBTreeNode<KEY, VALUE>*& _pHead, RBTreeNode<KEY, VALUE>*& _pTail)
{
RBTreeNode<KEY, VALUE>* pTarget = _pHead;
RBTreeNode<KEY, VALUE>* pTmp = nullptr;
_pHead = nullptr;
_pTail = nullptr;
while (pTarget)
{
pTmp = pTarget;
pTarget = pTarget->m_pNext;
delete pTmp;
}
}
template<class KEY, class VALUE>
void RBTREE::RBTree<KEY, VALUE>::ListDestroy()
{
list_destroy(m_pHead, m_pTail);
m_pRoot = nullptr;
}
template<class KEY, class VALUE>
RBTREE::RBTree<KEY, VALUE>::list_iterator::list_iterator(RBTreeNode<KEY, VALUE>* _pListNode):m_pIndex(_pListNode){}
template<class KEY, class VALUE>
RBTREE::RBTree<KEY, VALUE>::list_iterator::~list_iterator(){}
template<class KEY, class VALUE>
bool RBTREE::RBTree<KEY, VALUE>::list_iterator::operator!=(RBTreeNode<KEY, VALUE>* _pListNode)
{
return m_pIndex != _pListNode;
}
template<class KEY, class VALUE>
typename RBTREE::RBTree<KEY,VALUE>::list_iterator& RBTREE::RBTree<KEY,VALUE>::list_iterator::operator++()
{
m_pIndex = m_pIndex->m_pNext;
return *this;
}
template<class KEY, class VALUE>
typename RBTREE::RBTree<KEY, VALUE>::list_iterator& RBTREE::RBTree<KEY, VALUE>::list_iterator::operator++(int)
{
list_iterator iter = *this;
m_pIndex = m_pIndex->m_pNext;
return iter;
}
#endif // !__RBTREE_H__
我已经修复完成了,再帮我详细看看