根据我给你的代码,帮我扩展一个swap函数出来#ifndef _RBTREE_H_
#define _RBTREE_H_
/*
红黑树性质 验证方法
1. 节点为红或黑 无需显式检查(由颜色枚举保证)
2. 根节点为黑 在入口函数检查 root->_col == BLACK
3. 叶子节点(NIL)为黑 通过 nullptr 隐式处理(到达空节点时统计)
4. 红色节点不能有红子节点 递归检查父子节点颜色
5. 黑高一致性 对比每条路径的黑色节点数是否等于 refNum
*/
/*
性质:
1.每个节点要么是红色, 要么就是黑色.
2.根节点必为黑色。
3.叶子节点(NIL)都为黑色,数据key和value且为null。
4.红色节点的两个子节点都为黑色(红黑树不会出现相邻的红色节点)(即父与子节点不可能都是红色)
5.从任意节点出发,到其每个叶子节点(NIL)的路径中包含相同数量的黑色节点
衍生特性:
1.新加入到红黑树的节点为红色节点,如果新加入节点是黑色,会影响性质4。所以新节点是红色影响最小
2.根据性质3判断: 叶子节点(NIL)虽然是null, 但也算是一个有效的颜色节点
3.根据性质4判断: 黑色节点的2个子节点可以为2个红色节点; 也可以是2个黑色节点; 也可以是1红1黑的节点
5.根据性质5判断: 确保没有一条路径会比其他路径长出俩倍
*/
#include <type_traits>
namespace RBTREE
{
//红黑树的颜色类型
typedef enum class tagCOLOR : unsigned char//给枚举类型限定字节数,C++11开始支持 sizeof(Color) = 1byte
{
RED, BLACK
}COLOR;
/****************************************************************************************************************************/
//红黑树左右孩子的位置类型,主要用作判断当前节点是属于父节点的左孩子还是右孩子
//这样就不用疯狂使用父节点寻址才能得到当前节点的分支方向(左右)
typedef enum class tagBELONG : unsigned char//给枚举类型限定字节数,C++11开始支持 sizeof(BELONG) = 1byte
{
ROOT, LEFT, RIGHT, NIL
}BELONG;
/****************************************************************************************************************************/
//红黑树节点类
template<class KEY, class VALUE>
class RBTreeNode
{
public:
COLOR m_Color; //节点颜色,不是红就是黑. 默认色是红色,黑色会影响性质5
KEY m_Key; //关键值: 查找;比较... 都依靠它
VALUE m_Value; //目标元素
RBTreeNode* m_pParent; //父节点
RBTreeNode* m_pLeft; //左孩子
RBTreeNode* m_pRight; //右孩子
BELONG m_beLong; //节点是属于父节点的左孩子还是右孩子。
static RBTreeNode<KEY, VALUE> NIL; // 静态NIL声明
//默认构造函数:仅供NIL叶子节点创建时使用
RBTreeNode() :
m_Color(COLOR::BLACK), //NIL节点默认颜色是: 黑色
m_Key(), //KEY默认构造
m_Value(), //VALUE默认构造
m_pParent(nullptr), //父指针置空
m_pLeft(nullptr), //左指针置空
m_pRight(nullptr), //右指针置空
m_beLong(BELONG::NIL) //设置为叶子节点NIL
{
}
//初始化节点数据
RBTreeNode(const KEY& _Key, const VALUE& _Value) :
m_Color(COLOR::RED), //新节点默认颜色是: 红色
m_Key(_Key), //启动KEY的复制构造函数
m_Value(_Value), //启动VALUE的复制构造函数
m_pParent(nullptr), //父指针置空
m_pLeft(nullptr), //左指针置空
m_pRight(nullptr), //右指针置空
m_beLong(BELONG::ROOT) //设置为根节点,插入/删除后会调整
{
//强制定义叶子节点为的就是维护红黑树的性质: 2.根据性质3判断: 叶子节点(NIL)虽然是null, 但也算是一个有效的颜色节点
m_pRight = &NIL; //新节点的右边指向叶子节点
m_pLeft = &NIL; //新节点的左边指向叶子节点
NIL.m_pLeft = NIL.m_pRight = NIL.m_pParent = &NIL; //完全自引用
//其实可以直接用nullptr去模拟成NIL节点,下面有2个方案的解析说明: 推荐方案:优先使用实质NIL节点
/*
方案一:使用nullptr表示NIL节点
实现方式
优点
内存效率高
不需要为NIL节点分配额外内存,节省空间。
适合内存敏感场景(如嵌入式系统)。
代码简洁
直接使用nullptr判断,减少间接访问。
例如:if(node->m_pLeft == nullptr) 比 if (node->m_pLeft == m_NIL) 更直观。
性能略优
避免指针解引用(访问实质NIL节点需要一次内存访问)。
缺点
边界条件处理复杂
旋转、删除等操作需要频繁检查nullptr,容易遗漏边界条件。
例如:左旋时需确保node->m_pRight != nullptr。
代码重复
每个操作需重复nullptr检查,可能违反DRY原则。
调试困难
nullptr无法提供调试信息(如NIL节点的颜色、父节点等)。
方案二:使用实质NIL节点
实现方式
优点
统一边界条件处理
所有叶子节点均为m_NIL,简化旋转、删除等操作的逻辑。
例如:左旋时无需检查node->m_pRight是否为空。
代码可维护性强
通过isNIL()方法封装判断逻辑,减少重复代码。
调试时可打印NIL节点的信息(如颜色、父节点)。
符合红黑树理论定义
明确区分内部节点和叶子节点,便于验证红黑树性质。
支持复杂自定义类型
自定义类型的NIL节点可初始化默认值,避免未定义行为。
缺点
内存开销
每个红黑树实例需分配一个NIL节点(通常可忽略)。
初始化复杂度
需确保NIL节点的指针自洽(如m_pLeft = m_pRight = m_pParent = this)。
推荐方案:优先使用实质NIL节点
理由
安全性:避免因nullptr检查遗漏导致的未定义行为(如空指针解引用)。
可维护性:统一处理边界条件,减少代码错误。
调试友好:NIL节点可附加调试信息(如颜色、父节点)。
性能差异极小:现代CPU的缓存机制会抵消单次指针解引用的开销。
*/
}
//判断当前是否叶子节点
inline bool isNIL()const { return m_beLong == BELONG::NIL; }
};
// 类外静态成员定义
template<class KEY, class VALUE>
RBTreeNode<KEY, VALUE> RBTreeNode<KEY, VALUE>::NIL;
/****************************************************************************************************************************/
//红黑树类
template<class KEY, class VALUE>
class RBTree
{
private:
RBTreeNode<KEY, VALUE>* m_pRoot;//红黑树根节点, 性质: 必须为黑色
public:
//默认构造函数
RBTree() :
m_pRoot(nullptr) //新创建的树,根节点置为空
{
}
//默认析构函数
~RBTree()
{
}
//判断是否空树
inline bool IsEmpty()const { return m_pRoot == nullptr; }
//向红黑树插入元素
bool Insert(const KEY& _Key, const VALUE& _Value)
{
RBTreeNode<KEY, VALUE>* pNewNode = new RBTreeNode<KEY, VALUE>(_Key, _Value);//创建一个新节点
if (insert(m_pRoot, pNewNode)) //接入内部insert函数
{
return true;//返回true
}
delete pNewNode;//插入失败,删除新节点
pNewNode = NULL;
return false;
}
//精确查找
VALUE* Find(const KEY& _Key)
{
RBTreeNode<KEY, VALUE>* pResult = find(_Key);
return pResult ? &pResult->m_Value : nullptr;
}
bool Erase(const KEY& _Key)
{
RBTreeNode<KEY, VALUE>* pResult = find(_Key);
if (pResult)
{
return erase(pResult);
}
//查找不到指定key
return false;
}
private: //红黑树的内部函数
// 1. 将红黑树当作一颗二叉查找树,将新节点插入到红黑树中。
// 2. 插入完成后再调整树状态,使其能成为一棵合法的红黑树
bool insert(RBTreeNode<KEY, VALUE>* &_pRoot, RBTreeNode<KEY, VALUE>* _pNewNode) //&_pRoot能够使函数内部直接读写m_pRoot
{
if (_pNewNode == nullptr)return false;
RBTreeNode<KEY, VALUE>* pCursor = _pRoot;//操作游标,一般指当前节点
RBTreeNode<KEY, VALUE>* pPreNode = nullptr;//用作 备份上一级节点.当游标指向NIL叶子节点时,此指针就能直接向上回溯出父节点
BELONG blNIL = BELONG::NIL; //用来判断找到的叶子节点是左孩子或者是右孩子
while (pCursor != nullptr && !pCursor->isNIL())//如果当前节点不为空, 并且不是叶子节点
{
pPreNode = pCursor;//备份上一个节点,游标将指向下个节点
if (_pNewNode->m_Key < pCursor->m_Key)//如果新节点的key小于当前节点的key
{
pCursor = pCursor->m_pLeft;//把左孩子赋值给游标
blNIL = BELONG::LEFT;//最后比较是: 向左操作
continue;//重新循环比较
}
else if (_pNewNode->m_Key > pCursor->m_Key)//如果新节点的key大于当前节点的key
{
pCursor = pCursor->m_pRight;//把右孩子赋值给游标
blNIL = BELONG::RIGHT;//最后比较是: 向右操作
continue;//重新循环比较
}
else //那么说明插入的节点已经存在
{
return false;//插入失败
}
}
/*代码执行到此, 只有以下2种情况:
1. m_pRoot是空的
2. 说明游标节点已经指向到: 叶子节点*/
if (pCursor == nullptr)//m_pRoot是空的
{
_pRoot = _pNewNode; //当前节点为根节点
_pRoot->m_Color = COLOR::BLACK; //根节点要求绝对黑色
_pRoot->m_beLong = BELONG::ROOT; //设置为根节点
return true;
}
else if(pCursor->isNIL() && pPreNode)//2.游标指向NIL, 说明已经找到适合插入的节点, 并且父节点不为空
{
//判断最后的比较操作
if (blNIL == BELONG::LEFT)//叶子节点是位于左边
{
//开始与左节点进行联结
pPreNode->m_pLeft = _pNewNode;//父节点的左孩子设置成这个新节点
_pNewNode->m_pParent = pPreNode;//设置这个新节点的父亲
_pNewNode->m_beLong = BELONG::LEFT;//这新节点是父亲的左孩子
//调整颜色不在此处调整
//联结完成
}
else if (blNIL == BELONG::RIGHT)//叶子节点是位于右边
{
//开始与右节点进行联结
pPreNode->m_pRight = _pNewNode;//父节点的右孩子设置成这个新节点
_pNewNode->m_pParent = pPreNode;//设置这个新节点的父亲
_pNewNode->m_beLong = BELONG::RIGHT;//这新节点是父亲的右孩子
//调整颜色不在此处调整
//联结完成
}
else
{
//不应该也不可能来到此处,属于异常: 不是左操作,也不是右操作,等于操作已经返回false了
return false;
}
}
else
{
//不应该来到此处,属于异常: 游标指向是叶子节点, 但pPreNode是空,这并不可能发生的事
return false;
}
//代码执行到此: 需要调整颜色调整树结构了, 审查当前结构的合法性
//设置插入目标节点的颜色为: 红色. 插入新节点为红色对红黑树的性质5影响最小
//_pNewNode->m_Color = Color::RED;//节点初始化已经是红色,无须重复设置
//插入新节点后, 重新修正为一颗平衡二叉树
insert_fix(_pNewNode); //////////
//修正完成后就是一棵标准的: 红黑树 //
//
return true; //
} //
//
/******************************************开始解析说明 insert_fix() 函数修复红黑树过程********************************
* 红黑树插入修正函数
*
* 在向红黑树中插入节点之后(失去平衡),再调用该函数;
* 目的是将它根据颜色调整重新塑造成一颗红黑树。
*
* 参数说明:
* _pRoot 红黑树的根节点
* _pNewNode 插入的新节点
*/
/*
修正原理
回忆下红黑树的性质:
性质:
1.每个节点要么是红色, 要么就是黑色。
2.根节点必为黑色。
3.叶子节点(NIL)都为黑色,数据key和value且为null。
4.红色节点的两个子节点都为黑色(红黑树不会出现相邻的红色节点)(即父与子节点不可能都是红色)
5.从任意节点出发,到其每个叶子节点(NIL)的路径中包含相同数量的黑色节点
衍生特性:
1.新加入到红黑树的节点为红色节点,如果新加入节点是黑色,会影响性质4。所以新节点是红色影响最小
2.根据性质3判断: 叶子节点(NIL)虽然key和value是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处理方案:
将父新节点设置成黑色
将祖父节点设置成红色
将祖父节点进行左旋操作
*/
bool insert_fix(RBTreeNode<KEY, VALUE>* _pNewNode)
{
if (!_pNewNode)return false;
RBTreeNode<KEY, VALUE>* pParent = _pNewNode->m_pParent; //取得父节点
RBTreeNode<KEY, VALUE>* pGrandfather = nullptr;//取出祖父节点
RBTreeNode<KEY, VALUE>* pUncle = nullptr; //用作储存叔叔节点
// 情景1: 插入的节点的父节点是黑色
// 情景1处理方案:无需任何处理
if (pParent && pParent->m_Color == COLOR::BLACK)return true;//情景1处理完成
if (!pParent)//说明已经到达根节点了
{
if (_pNewNode->m_beLong == BELONG::ROOT)
{
_pNewNode->m_Color = COLOR::BLACK;//根节点强制黑色
return true;
}
return false;
}
do
{
//这是情景2: 插入的新节点的父亲是红色的,违反红黑树性质4:
//4. 红色节点的两个子节点都为黑色(红黑树不会出现相邻的红色节点)(即父与子节点不可能都是红色)
pGrandfather = pParent->m_pParent;//取出祖父节点
if (pGrandfather)//祖父节点是有效的
{
//如果父亲是左孩子,那么叔叔肯定是祖父的右孩子。否则父亲是右孩子,那么叔叔就是左孩子
pUncle = pParent->m_beLong == BELONG::LEFT ? pGrandfather->m_pRight : pParent->m_beLong == BELONG::RIGHT ? pGrandfather->m_pLeft : nullptr;//取出叔叔节点
if (pUncle == nullptr)
{
//叔叔节点居然提取不到,不可能的,说明结构有异常
throw("红黑树结构异常: 颜色持衡过程发现->取得叔叔节点失败, 父亲节点居然是NIL");
}
//开始检查叔叔节点颜色
if (pUncle->m_Color == COLOR::RED)//叔叔节点是红色: 这是情景2.1
{
//情景2.1修正方案: 颜色持衡
//将P设置为黑色
//将U设置为黑色
//将G设置为红色
//将G设置成当前节点继续while循环向上判断
pParent->m_Color = COLOR::BLACK;//将P设置为黑色
pUncle->m_Color = COLOR::BLACK; //将U设置为黑色
pGrandfather->m_Color = COLOR::RED;//将G设置为红色
return insert_fix(pGrandfather);//将G设置成当前节点继续递归循环向上判断
}
else if(pUncle->m_Color == COLOR::BLACK)//叔叔节点是黑色: 这是情景2.2或者情景2.3
{
if (pParent->m_beLong == BELONG::LEFT)//父亲是个左孩子,触发情景2.2
{
if (!_pNewNode)
{
throw("触发情景2.2: 插入节点为空");
}
if (_pNewNode->m_beLong == BELONG::RIGHT)//如果插入节点是其父亲的右孩子: 触发情景 2.2.1
{
pParent = left_rotate(pParent);//对父亲进行左旋操作得到情景2.2.2
if (!pParent)throw("红黑树结构异常: 触发情景2.2.1时左旋失败");
_pNewNode = pParent->m_pLeft;
//左旋后, 插入节点和父亲交换成功了。
return insert_fix(_pNewNode);//重新递归进入情景: 2.2.2
}
else if (_pNewNode->m_beLong == BELONG::LEFT)//如果插入节点是其父亲的左孩子: 触发情景2.2.2
{
pParent->m_Color = COLOR::BLACK;//将P设置成黑色
pGrandfather->m_Color = COLOR::RED;//将G设置成红色
pParent = right_rotate(pGrandfather);//对G进行右旋操作
return true;//情景2.2的所有场景解析完成
}
//进入到此处代码说明 新插入节点是ROOT,NIL, else已经不可能了,因为insert_fix的上层是insert对pNewNode封装好了
}
else if (pParent->m_beLong == BELONG::RIGHT)//父亲是个右孩子,触发情景2.3
{
if (!_pNewNode)
{
throw("触发情景2.3: 插入节点为空");
}
if (_pNewNode->m_beLong == BELONG::LEFT)//如果插入的节点是其父亲的左孩子: 触发情景2.3.1
{
pParent = right_rotate(pParent);//对父亲进行右旋操作得到情景2.3.2
if(!pParent)throw("红黑树结构异常: 触发情景2.3.1时右旋失败");
_pNewNode = pParent->m_pRight;
//右旋后,插入节点和父亲交换成功了
return insert_fix(_pNewNode);//递归 进入情景: 2.3.2
}
else if (_pNewNode->m_beLong == BELONG::RIGHT)//如果插入节点是其父亲的右孩子: 触发情景2.3.2
{
pParent->m_Color = COLOR::BLACK;//将P设置成黑色
pGrandfather->m_Color = COLOR::RED;//将G设置成红色
pParent = left_rotate(pGrandfather);//对G进行左旋操作
return true;//情景2.3的所有场景解析完成
}
//进入到此处代码说明 新插入节点是ROOT,NIL, else已经不可能了,因为insert_fix的上层是insert对pNewNode封装好了
}
else if (pParent->m_beLong == BELONG::ROOT)
{
//插入节点红色,父节点红色才会进入情景2.2-2.3,但父亲如果是根节点Root,那么之前这个红黑树就是不合法了
//因为根节点永远是黑色,所以其实能进入到此处也是异常了。
//pParent->m_Color = COLOR::BLACK;//无条件把根节点设置为黑色
//return true;//已经while到根节点Root了
throw("红黑树结构异常: 插入红色新节点,父节点也是红色,但父节点居然是个根节点,红色的根节点是异常的");
}
else
{
//基本不可能进入到此处,因为叶子节点永远是黑色的
throw("红黑树结构异常: 插入红色新节点,父节点也是红色,但父节点居然是个叶子节点");
}
}
//else 基本不可能的,因为红黑树性质: 节点不是红就是黑, 不可能进入else了
}
else//祖父节点是无效的: 说明只有一种情况:当前的_pParent节点是根节点Root
{
if (pParent->m_beLong == BELONG::ROOT)//当前父节点是根节点Root
{
pParent->m_Color = COLOR::BLACK;//无条件把根节点设置为黑色
return true;//已经while到根节点Root了
}
else
{
//祖父节点不存在, 父节点又不是根节点,说明结构异常了
throw("红黑树结构异常: 颜色持衡过程发现->祖父节点为空, 但父节点又不是根节点Root,这情况不应该发生.");
}
}
} while (pParent);
return false;
}
//进行左旋操作
RBTreeNode<KEY, VALUE>* left_rotate(RBTreeNode<KEY, VALUE>* _pTarget)
{
if (!_pTarget)return nullptr;
if (_pTarget->isNIL())
{
throw("左旋异常: 目标是个NIL, 叶子节点不能左旋操作");
}
RBTreeNode<KEY, VALUE>* pParent = _pTarget->m_pParent; //取出目标的父节点
RBTreeNode<KEY, VALUE>* pRight = _pTarget->m_pRight;//备份目标的右孩子
RBTreeNode<KEY, VALUE>* pTmp = nullptr;//临时操作使用
BELONG beLong = _pTarget->m_beLong;//备份目标的 左/右孩子属性
if (pRight->isNIL())
{
throw("左旋异常: 目标的右孩子是NIL, NIL不可能作为父节点的");
}
pTmp = _pTarget->m_pRight = pRight->m_pLeft;//重定向右孩子到备份pRight的左孩子
pRight->m_pLeft = nullptr;//把备份的pRight的左孩子断开,因为左孩子已经被目标节点重定向占用了
if (pTmp->isNIL() == false)//如果新定向的右孩子不是NIL叶子节点
{
pTmp->m_beLong = BELONG::RIGHT;//这个新孩子是从备份的pRight左边取过来的变成右孩子
pTmp->m_pParent = _pTarget;//把这个新右孩子的父亲设置是目标节点
}
//else //新的右孩子是个NIL叶子节点,不用设置NIL属性
pTmp = nullptr;//置空,避免后面误操作
pRight->m_pLeft = _pTarget;//重新设置备份pRight的左孩子为目标节点
_pTarget->m_pParent = pRight;//重设置目标的父亲为pRight
_pTarget->m_beLong = BELONG::LEFT;//此时目标就是pRight的左孩子
if (pParent == nullptr)//左旋的目标没有父节点,说明左旋的目标是一个根节点
{
if (beLong != BELONG::ROOT)//如果成立,说明之前红黑树结构异常
{
//左旋目标没有父节点并且不是根节点,异常了
throw("左旋异常: 目标没有父节点并且也没有根节点的标记");
}
pRight->m_beLong = beLong;//设置Root
pRight->m_Color = COLOR::BLACK;//根节点必然为黑
pRight->m_pParent = nullptr;//根节点没有父亲
m_pRoot = pRight;//根节点已经发生变化
}
else
{
pRight->m_beLong = beLong;//设置备份pRight的孩子属性
pRight->m_pParent = pParent;//设置备份pRight的父亲
if (beLong == BELONG::LEFT)//左旋目标原来是一个左孩子
{
pParent->m_pLeft = pRight;
}
else//左旋目标原来是一个右孩子
{
pParent->m_pRight = pRight;
}
}
return pRight;
}
//进行左旋操作
RBTreeNode<KEY, VALUE>* right_rotate(RBTreeNode<KEY, VALUE>* _pTarget)
{
if (!_pTarget)return nullptr;
if (_pTarget->isNIL())
{
throw("右旋异常: 目标是个NIL, 叶子节点不能右旋操作");
}
RBTreeNode<KEY, VALUE>* pParent = _pTarget->m_pParent; //取出目标的父节点
RBTreeNode<KEY, VALUE>* pLeft = _pTarget->m_pLeft;//备份目标的左孩子
RBTreeNode<KEY, VALUE>* pTmp = nullptr;//临时操作使用
BELONG beLong = _pTarget->m_beLong;//备份目标的 左/右孩子属性
if (pLeft->isNIL())
{
throw("右旋异常: 目标的左孩子是NIL, NIL不可能作为父节点的");
}
pTmp = _pTarget->m_pLeft = pLeft->m_pRight;//重定向左孩子到备份pLeft的右孩子
pLeft->m_pRight = nullptr;//把备份的pLeft的右孩子断开,因为该右孩子已经被目标节点重定向占用了
if (pTmp->isNIL() == false)//如果新定向的左孩子不是NIL叶子节点
{
pTmp->m_beLong = BELONG::LEFT;//这个新孩子是从备份的pLeft右边取过来的变成左孩子的
pTmp->m_pParent = _pTarget;//把这个新左孩子的父亲设置是目标节点
}
//else //新的左孩子是个NIL叶子节点,不用设置NIL属性
pTmp = nullptr;//置空,避免后面误操作
pLeft->m_pRight = _pTarget;//重新设置备份pLeft的右孩子为目标节点
_pTarget->m_pParent = pLeft;//重设置目标的父亲为pLeft
_pTarget->m_beLong = BELONG::RIGHT;//此时目标就是pLeft的右孩子
if (pParent == nullptr)//右旋的目标没有父节点,说明右旋的目标是一个根节点
{
if (beLong != BELONG::ROOT)//如果成立,说明之前红黑树结构异常
{
//右旋目标没有父节点并且不是根节点,异常了
throw("右旋异常: 目标没有父节点并且也没有根节点的标记");
}
pLeft->m_beLong = beLong;//设置Root
pLeft->m_Color = COLOR::BLACK;//根节点必然为黑
pLeft->m_pParent = nullptr;//根节点没有父亲
m_pRoot = pLeft;//根节点已经发生变化
}
else
{
pLeft->m_beLong = beLong;//设置备份pLeft的孩子属性
pLeft->m_pParent = pParent;//设置备份pLeft的父亲
if (beLong == BELONG::LEFT)//右旋目标原来是一个左孩子
{
pParent->m_pLeft = pLeft;
}
else//左旋目标原来是一个右孩子
{
pParent->m_pRight = pLeft;
}
}
return pLeft;
}
// 精确查找(返回节点指针,未找到返回nullptr)
RBTreeNode<KEY, VALUE>* find(const KEY& key) const
{
RBTreeNode<KEY, VALUE>* pCurrent = m_pRoot;
while (pCurrent && !pCurrent->isNIL())
{
if (key == pCurrent->m_Key)
{
return pCurrent;
}
else if (key < pCurrent->m_Key)
{
pCurrent = pCurrent->m_pLeft;
}
else
{
pCurrent = pCurrent->m_pRight;
}
}
return nullptr;
}
//删除操作
bool erase(RBTreeNode<KEY, VALUE>* _pTarget)
{
if (!_pTarget)return false;
RBTreeNode<KEY, VALUE>* pParent = _pTarget->m_pParent;
RBTreeNode<KEY, VALUE>* pBrother = nullptr;
if (_pTarget->m_Color == COLOR::RED)//场景1:被删除节点是红色
{
return remove_node_red(_pTarget);//转入场景1函数独立处理
}
else//被删除的节点是黑色
{
if (!pParent)
{
//没有父亲,删除的节点是根节点
return false;
}
pBrother = _pTarget->m_beLong == BELONG::LEFT ? pParent->m_pRight : pParent->m_pLeft;
if (pBrother->m_Color == COLOR::RED)//场景2:被删除节点是黑色,并且兄弟是红色
{
}
else//场景3:被删除节点是黑色,并且兄弟是黑色
{
}
}
}
bool remove_node_red(RBTreeNode<KEY, VALUE>* _pTarget)
{
RBTreeNode<KEY, VALUE>* pParent = _pTarget->m_pParent;//红节点必然拥有父节点的,没有父亲说明是根节点,红色根节点?
if (_pTarget->m_pLeft->isNIL() && _pTarget->m_pRight->isNIL())
{
//场景1.1
if (_pTarget->m_beLong == BELONG::LEFT)//删除的目标是个左孩子
{
pParent->m_pLeft = _pTarget->m_pLeft;//把父亲的左孩子定向到NIL节点
}
else//删除的目标是右孩子
{
pParent->m_pRight = _pTarget->m_pRight;//把父亲的右孩子定向到NIL节点
}
_pTarget->m_pParent = nullptr;//和父亲正式断开联结
delete _pTarget; //删除目标节点
_pTarget = nullptr;
return true;
}
else//2个子节点都是黑色: 红节点下面不可能只有一个黑色节点的
{
//场景1.2
}
return false;
}
};
}
#endif // !_RBTREE_H_
最新发布