红黑树
红黑树是一棵二叉搜索树,它在每一个节点上增加了一个表示颜色的指针,红色或者黑色。
一棵红黑树是满足下面性质的二叉搜索树:
- 每个节点非红即黑
- 根结点是黑色的
- 每个叶节点(这里的叶节点是指NULL节点)是黑色的
- 如果一个节点是红的,那么两个子节点都是黑的
- 对于任一节点,从该节点到所有后代叶节点的路径上,黑色节点的数目相等
由于这些性质的约定,红黑树没有一条路径会比其他路径长出两倍,因而红黑树是近似平衡的。
红黑树在插入和删除时会对树进行修改,可能会违反上面列出的红黑树的性质,因此需要对树作出相应的调整。
插入
说明:为了满足性质5,我们默认新增节点的颜色为红色
并且约定:cur为当前插入节点、p为插入节点父节点、g为祖父节点,u为叔叔节点。
情况一
若树为空,为了满足性质2,需将新增节点改成黑色
情况二
插入节点的父节点为黑色,直接插入
情况三
插入节点的父节点为红色
这里根据叔叔节点的不同又分两种情况讨论:
1)叔叔节点存在且为红
调整方法:将p和u改成黑,g改成红,然后把p当成cur继续向上迭代调整。
2)叔叔节点不存在或着为黑
根据p和cur的位置的不同分为四种情况:
①p为g的左孩子,cur为p的左孩子
调整方法:进行右单旋,并且p变为黑色,g变为红色,此时经过调整后的根结点已经为黑色,故不需要向上调整

②p为g的右孩子,cur为p的右孩子
调整方法:进行左单旋,并且p变为黑色,g变为红色,此时经过调整后的根结点已经为黑色,故不需要向上调整

③p为g的左孩子,cur为p的右孩子
调整方法:针对p进行左单旋,从而转化为情况①

④p为g的右孩子,cur为p的左孩子
调整方法:针对p进行右单旋,从而转化为情况②

删除
红黑树的删除和二叉搜索树的删除操作类似,但是红黑树在删除节点后需要进行相应的调整,使其仍然满足红黑树的性质。当被删除节点左右节点都不为空的情况下,我们需要在其右子树寻找最左节点或者在其左子树寻找最右边的节点作为真正要被删除的替换节点(金蝉脱壳),被删节点与替换节点交换后,把替换节点删除即可。
注意,此时待删除的节点要么没有孩子节点,要么只有左子树或者只有右子树
下面主要说明在删除替换节点后,怎样重新调整红黑树
情况一
删除的节点为红色节点,直接删除
情况二
删除的节点为黑色,显然删除一个黑色节点后会违反性质5,需要进行调整,
1)有孩子节点并且孩子节点为红色
直接删除节点并把孩子节点变为黑色
注意要删除节点为根结点的情况:
2)无孩子节或者孩子节点为黑色(事实上孩子节点为黑的情况一定不存在)
分为以下五种情况:
注:约定N为待删节点,P为父节点,S为兄弟节点
①S为红色(p和S的子节点一定为黑),N是P的左孩子(或者右孩子)
操作:以S为轴心左旋,并且把S变为黑,P变为红
此时删除N节点后,在N节点的路径上少了一个黑节点,因此需要以N为开始重新向上调整
②P和S都为黑,且S的孩子们也都为黑
操作:S变为红色
此时删除S变色后,并且删除N后,以P为根结点的子树的黑节点减少了两个,因此需要以P为开始重新向上调整
③P为红(S一定为黑),S的孩子们为黑
操作:P改为黑,S改为红
此时,在删除N节点后黑节点的数目无变化,无需调整
④P为任意颜色,S为黑,N是P的左孩子,S的右孩子SR为红(或者是N是P的右孩子,S的左孩子为红,S的右孩子任意)
操作:以S为中心左旋,SR(SL)改为黑,P改为黑,S改为P的颜色
此时,删除N后,黑节点数目无变化,无需调整
⑤P任意色,S为黑,N是P的左孩子,S的左孩子SL为红,S的右孩子SR为黑(或者N是P的有孩子,S的右孩子为红,S的左孩子为黑)
操作:以SL为中心进行左旋,并且S变为红,SL变为黑
此时,变成了情况④
至此,删除的所有情况已经分析完毕。
#pragma once
#include<utility>
#include<algorithm>
#include<iostream>
using namespace std;
enum Color
{
RED = 0,
BLACK
};
template<typename K, typename V>
struct RBTreeNode
{
RBTreeNode* _pLeft;
RBTreeNode* _pRight;
RBTreeNode* _pParent;
Color _color;
pair<K, V> _v;
RBTreeNode(const K& key, const V& value)
:_pLeft(NULL)
, _pRight(NULL)
, _pParent(NULL)
, _color(RED)
, _v(pair<K, V>(key, value))
{}
};
template<typename K, typename V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
typedef Node* pNode;
public:
RBTree()
:_pRoot(NULL)
{}
bool insert(const K& key, const V& value)
{
if (NULL == _pRoot)
{
_pRoot = new Node(key, value);
_pRoot->_color = BLACK;
return true;
}
pNode pCur = _pRoot;
pNode pParent = NULL;
while (pCur)
{
pParent = pCur;
if (pCur->_v.first > key)
pCur = pCur->_pLeft;
else if (pCur->_v.first < key)
pCur = pCur->_pRight;
else
return false;
}
pCur = new Node(key, value);
if (pParent->_v.first > key)
pParent->_pLeft = pCur;
else
pParent->_pRight = pCur;
pCur->_pParent = pParent;
if (BLACK == pParent->_color)
return true;
while (pCur != _pRoot && RED == pParent->_color)
{
pNode pGrdftr = pParent->_pParent;
//根据p位于g的方向
if (pParent == pGrdftr->_pLeft)
{
//叔叔是否存在,节点的颜色
pNode pUncle = pGrdftr->_pRight;
if (NULL != pUncle && RED == pUncle->_color)
{
pParent->_color = BLACK;
pUncle->_color = BLACK;
pGrdftr->_color = RED;
pCur = pGrdftr;
pParent = pCur->_pParent;
}
else
{
//根据cur在p的哪边
if (pCur == pParent->_pLeft)
{
_RotateRight(pParent->_pParent);
pParent->_color = BLACK;
pGrdftr->_color = RED;
}
else
{
_RotateLeft(pParent);
swap(pParent, pCur);
}
}
}
else
{
//叔叔是否存在,节点的颜色
pNode pUncle = pGrdftr->_pLeft;
if (NULL != pUncle && RED == pUncle->_color)
{
pParent->_color = BLACK;
pUncle->_color = BLACK;
pGrdftr->_color = RED;
pCur = pGrdftr;
pParent = pCur->_pParent;
}
else
{
//根据cur在p的哪边
if (pCur == pParent->_pRight)
{
_RotateLeft(pParent->_pParent);
pParent->_color = BLACK;
pGrdftr->_color = RED;
}
else
{
_RotateRight(pParent);
swap(pParent, pCur);
}
}
}
}
_pRoot->_color = BLACK;
return true;
}
bool IsRBTree()
{
if (NULL == _pRoot)
return true;
if (BLACK != _pRoot->_color)
return false;
int BlackNum = 0;
int CurBlackNum = 0;
pNode pCur = _pRoot;
//先统计出随便一路径的黑色节点的个数
while (pCur)
{
if (BLACK == pCur->_color)
BlackNum++;
pCur = pCur->_pLeft;
}
return _IsRBTree(_pRoot,BlackNum,CurBlackNum);
}
bool Find(const K& key)
{
pNode pCur = _pRoot;
while (pCur)
{
if (key == pCur->_v.first)
return true;
else if (key < pCur->_v.first)
pCur = pCur->_pRight;
else
pCur = pCur->_pLeft;
}
return false;
}
bool Remove(const K& key)
{
pNode pCur = _pRoot;
pNode pParent = NULL;
pNode pDel = NULL;
//寻找要删除的节点
while (pCur)
{
if (key == pCur->_v.first)
break;
else if (key < pCur->_v.first)
pCur = pCur->_pRight;
else
pCur = pCur->_pLeft;
}
//没找到要删节点
if (NULL == pCur)
return false;
//如果要删除节点有两个孩子节点,转移要删除节点
if (pCur->_pLeft != NULL && pCur->_pRight != NULL)
{
pNode pRightMin = pCur->_pRight;//找到右子树的最小节点
//即右子树的最左边节点
while (pRightMin->_pLeft)
pRightMin = pRightMin->_pLeft;
pCur->_v = pRightMin->_v;
pCur = pRightMin;//让pCur指向要删节点
}
pParent = pCur->_pParent;
pDel = pCur;
//要删除节点的左边为空
//此逻辑里面提前先处理好父节点的指向
//在后面的操作中直接删除即可
if (NULL == pDel->_pLeft)
{
//如果要删的是根结点
if (_pRoot == pDel)
{
_pRoot = pDel->_pRight;
//如果右孩子存在,那它一定为红色
if (NULL != pDel->_pRight)
{
_pRoot->_pParent = NULL;
_pRoot->_color = BLACK;
}
delete pDel;
return true;
}
//让被删节点的父节点指向被删节点的孩子节点
if (pParent->_pLeft == pDel)
pParent->_pLeft = pDel->_pRight;
else
pParent->_pRight = pDel->_pRight;
if (pDel->_pRight)
pDel->_pRight->_pParent = pParent;
pCur = pDel->_pRight;
}
else
{
//如果要删的是根结点
if (_pRoot == pDel)
_pRoot = pDel->_pLeft;
//如果左孩子存在,那它一定为红色
if (NULL != pDel->_pLeft)
{
_pRoot->_pParent = NULL;
_pRoot->_color = BLACK;
}
delete pDel;
return true;
//让被删节点的父节点指向被删节点的孩子节点
if (pParent->_pLeft == pDel)
pParent->_pLeft = pDel->_pRight;
else
pParent->_pRight = pDel->_pRight;
if (pDel->_pRight)
pDel->_pRight->_pParent = pParent;
pCur = pDel->_pLeft;//让pCur标记孩子节点
}
//如果要删除节点是红色,直接删除
if (pDel->_color == RED)
{
delete pDel;
return true;
}
//如果待删节点为黑的,且其孩子是红的,将孩子改为黑色即可
if (BLACK == pDel->_color && pCur && RED == pCur->_color)
{
pCur->_color = BLACK;
delete pDel;
return true;
}
//要删除节点是黑色的
while (pParent)
{
//要删除节点是父亲的左孩子
if (pParent->_pLeft == pDel)
{
pNode pSubR = pParent->_pRight;//parent的右孩子,一定存在
//右孩子是红色的,对应情况①,注意这里的pSubR在图片中表示为S
if (pSubR->_color == RED)
{
_RotateLeft(pParent);
pSubR->_color = BLACK;
pParent->_color = RED;
}
else//pSubR为黑色
{
pNode pSubRL = pSubR->_pLeft;
pNode pSubRR = pSubR->_pRight;
//parent为黑色
//(SubR双节点为空)或者(SubR有双节点并且双节点为黑)
//对应情况②
if (BLACK == pParent->_color && (NULL == pSubR->_pLeft && NULL == pSubR->_pRight)
|| (pSubRL && pSubRR && BLACK == pSubRL->_color && BLACK == pSubRR->_color))
{
pSubR->_color = RED;
pCur = pParent;
pParent = pCur->_pParent;
}
else
{
if (RED == pParent->_color)//父节点为红色
{
//如果SubR的孩子们为黑 或者没有孩子们
//对应情况③
if ((NULL == pSubRL && NULL == pSubRR) ||
(pSubRL && pSubRR && BLACK == pSubRL->_color && BLACK == pSubRR->_color))
{
pParent->_color = BLACK;
pSubR->_color = RED;
break;
}
}
//对应情况⑤
if (RED == pSubRL->_color)
{
_RotateRight(pSubR);
pSubR = pSubRL;
}
//对应情况④
_RotateLeft(pParent);
if (RED == pParent->_color)
pSubR->_color = RED;
else
pSubR->_color = BLACK;
pParent->_color = BLACK;
pSubR->_pRight->_color = BLACK;
break;
}
}
}
else//要删除节点是父节点右孩子的情况,与上面相反
{
pNode pSubL = pParent->_pLeft;
if (pSubL->_color == RED)
{
_RotateRight(pParent);
pSubL->_color = BLACK;
pParent->_color = RED;
}
else
{
pNode pSubLL = pSubL->_pLeft;
pNode pSubLR = pSubL->_pRight;
if (BLACK == pParent->_color && (NULL == pSubL->_pLeft && NULL == pSubL->_pRight)
|| (pSubLL && pSubLR && BLACK == pSubLL->_color && BLACK == pSubLR->_color))
{
pSubL->_color = RED;
pCur = pParent;
pParent = pCur->_pParent;
}
else
{
if (RED == pParent->_color)//父节点为红色
{
if ((NULL == pSubLL && NULL == pSubLR) ||
(pSubLL && pSubLR && BLACK == pSubLL->_color && BLACK == pSubLR->_color))
{
pParent->_color = BLACK;
pSubL->_color = RED;
break;
}
}
//对应情况⑤
if (RED == pSubLL->_color)
{
_RotateLeft(pSubL);
pSubL = pSubLR;
}
//对应情况④
_RotateRight(pParent);
if (RED == pParent->_color)
pSubL->_color = RED;
else
pSubL->_color = BLACK;
pParent->_color = BLACK;
pSubL->_pLeft->_color = BLACK;
break;
}
}
}
}
_pRoot->_color = BLACK;
delete pDel;
return true;
}
private:
bool _IsRBTree(pNode pRoot,int BlackNum,int CurBlackNum)
{
if (NULL == pRoot)
return (BlackNum == CurBlackNum);
if (RED == pRoot->_color && RED == pRoot->_pParent->_color)
return false;
if (BLACK == pRoot->_color)
CurBlackNum++;
return (_IsRBTree(pRoot->_pLeft, BlackNum, CurBlackNum) && _IsRBTree(pRoot->_pRight, BlackNum, CurBlackNum));
}
void _RotateRight(pNode& pParent)
{
pNode pSubL = pParent->_pLeft;
pNode ppParent = pParent->_pParent;
pParent->_pLeft = pSubL->_pRight;
if (pSubL->_pRight != NULL)
pSubL->_pRight->_pParent = pParent;
pSubL->_pRight = pParent;
pParent->_pParent = pSubL;
if (NULL == ppParent)
{
_pRoot = pSubL;
return;
}
else if (pParent == ppParent->_pLeft)
ppParent->_pLeft = pSubL;
else
ppParent->_pRight = pSubL;
pSubL->_pParent = ppParent;
}
void _RotateLeft(pNode& pParent)
{
pNode pSubR = pParent->_pRight;
pNode ppParent = pParent->_pParent;
pParent->_pRight = pSubR->_pLeft;
if (pSubR->_pLeft != NULL)
pSubR->_pLeft->_pParent = pParent;
pSubR->_pLeft = pParent;
pParent->_pParent = pSubR;
pSubR->_pParent = ppParent;
if (NULL== ppParent)
{
_pRoot = pSubR;
return;
}
else if (pParent == ppParent->_pLeft)
ppParent->_pLeft = pSubR;
else
ppParent->_pRight = pSubR;
}
private:
pNode _pRoot;
};
void TestInsert()
{
int arr[] = { 10, 20, 30, 70, 90, 40, 5,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
RBTree<int, int> rb;
for (int i = 0; i < sz; ++i)
{
rb.insert(arr[i], i);
}
rb.Remove(20);
if (rb.IsRBTree())
cout << "是红黑树" << endl;
else
cout << "非红黑树" << endl;
}
本文详细介绍了红黑树的特性,包括每个节点的红色或黑色属性,以及保持红黑树平衡的五个性质。在红黑树的插入过程中,针对不同情况进行了调整策略的说明,如插入节点为红色、父节点为黑色或红色的处理。删除操作中,重点讨论了删除红色节点和黑色节点的不同情况,以及如何通过旋转和颜色调整来维护红黑树的性质。文章还提及了C++代码实现的相关内容。
5145

被折叠的 条评论
为什么被折叠?



