删除过程
在不涉及调整时,类似于AVL树的调整,先寻找要删除的节点,再寻找替换他的节点,将其替换,而后删除替换的叶子节点。
下面我们先来看一下删除调整函数。前提是当前节点为黑色,进入删除调整函数(eraseAdjust)
删除调整的流程
变量定义
调整流程
第一步:
上图为右右型(父节点是祖父节点的右孩子,当前节点是父节点的右孩子,通过定义的变量left 和 childleft的真假判断)一共为四种类型,左左,左右,右左,右右类型。
左左类型、右左类型—>左旋(传入当前节点的父节点)
右右类型、左右类型–>右旋(传入当前节点的父节点)
再将左旋和右旋的返回值赋给祖父节点的左或右孩子(通过left的值)
第二步
特殊情况处理,先处理完,剩下的就是需要处理的一般情况。
第三步
有红孩子分为六种情况,如下图:
第四步
判断属于那种类型,此时可以确定的是:1.当前节点为黑色,2.前节点的孩子都为黑色 3.当前节点的兄弟节点为黑色
根据当前接待你兄弟节点的孩子颜色分类,分为四类如下图:
LBr0
兄弟节点的孩子都为黑色,分为左右两种情况,统一处理
LBr1.1
兄弟节点有一个红孩子。通俗来讲,就是当前节点的外侄子(兄弟节点的远孩子)如下图
LBr1.2
兄弟节点有一个红孩子,通俗讲是,内侄子(兄弟节点的近孩子)为红色
LBR2
兄弟节点有两个红孩子
到调整函数结束,经过调整之后,节点要么是红色,要么是黑色有一个红孩子
template<class K, class E>
void brTree<K,E>::eraseAdjust(brTreeNode<K,E> *gp,brTreeNode<K,E> *pp,brTreeNode<K,E> *p){
bool left;//判断当前结点父节点是左孩子还是右孩子
bool childLeft;//判断当前结点是左孩子还是右孩子
childLeft=p->element.first<pp->element.first?true:false;//判断当前结点是左孩子还是右孩子
//判断父节点是祖父节点的左孩子还是右孩子
left=pp->element.first < gp->element.first?true:false;
//判断父节点的颜色,若为黑色,父节点有红孩子发生暂缓,需要调整
if(pp->color=='b')
{
if(left){
gp->leftChild=childLeft?left_rotations(pp):right_rotations(pp);
gp->leftChild->color='b';
}
else{
gp->rightChild=childLeft?left_rotations(pp):right_rotations(pp);
gp->rightChild->color='b';
}
pp->color='r';
}
//父节点为头节点,孩子节点都为黑色,调整颜色
if(pp==root&&p->leftChild->color=='b'&&p->rightChild->color=='b')
{
p->color='r';
return;
}
//有红孩子,发生暂缓
if(p->leftChild->color=='r'||p->rightChild->color=='r')
return;
else{
if(childLeft)
{
if(pp->rightChild->rightChild->color=='b')
{ //情况1.1
if(pp->rightChild->leftChild->color=='b')
{
pp->rightChild->color='r';
}
//情况3.1
if(pp->rightChild->leftChild->color=='r')
{
pp->rightChild=right_rotations(pp->rightChild);
if(left)
gp->leftChild=left_rotations(pp);
else
gp->rightChild=left_rotations(pp);
}
}
//2.1 4.1
else{
if(left)
{
gp->leftChild=left_rotations(pp);
gp->leftChild->color='r';
gp->leftChild->rightChild->color='b';
}
else
{
gp->rightChild=left_rotations(pp);
gp->rightChild->color='r';
gp->rightChild->rightChild->color='b';
}
}
pp->color='b';
p->color='r';
}
else{
if(pp->leftChild->leftChild->color=='b')
{ //情况1.2
if(pp->leftChild->rightChild->color=='b')
{
pp->leftChild->color='r';
}
//情况3.2
if(pp->leftChild->rightChild->color=='r')
{
pp->leftChild=left_rotations(pp->leftChild);
if(left)
gp->leftChild=right_rotations(pp);
else
gp->rightChild=right_rotations(pp);
}
}
//2.2 4.2
else{
if(left)
{
if(gp==root)
gp=gp->rightChild;
gp->leftChild=right_rotations(pp);
gp->leftChild->color='r';
gp->leftChild->leftChild->color='b';
}
else{
if(gp==root)
gp=gp->rightChild;
gp->rightChild=right_rotations(pp);
gp->rightChild->color='r';
gp->rightChild->leftChild->color='b';}
}
pp->color='b';
p->color='r';
}
}
}
删除过程
具体过程:边寻找待删除节点边往下走,遇见黑色节点则调用调整函数,把当前节点调整为红色,若有替换节点则类似边寻找替换节点边调整黑色。
从上到下,找到要删除的点,为当前节点p。
···p有左孩子,在p的左孩子中找到最大的节点s,来替换当前的节点的值,将删除当前节点转换为删除s,此时s要么是红色叶子节点,要么是有一个左红孩子的黑色结点。
···若左边没有替换节点,则判断右孩子的情况,若有右孩子(肯定是红色节点),用右边替换节点s的值替换P节点,删除s.若没有右孩子,则该节点p就是一个待删除的红色叶子节点,直接删掉即可。
//自顶而下的删除
template<class K, class E>
void brTree<K,E>::erase(const K& theKey){
brTreeNode<K,E> *p = root->rightChild,*pp = root,*gp=&nullNode;
while(p != &nullNode && p->element.first != theKey){//注意两个句子位置不能颠倒
if(p->color=='b'){
eraseAdjust(gp,pp,p);
}
gp=pp;
pp = p; // move to a child of p
if (theKey < p->element.first)
p = p->leftChild;
else
p = p->rightChild;
}//找到了删除的节点
if (p == &nullNode)//没找到
{
return;
}
if(p->color=='b'){
eraseAdjust(gp,pp,p);
}
if (p->leftChild !=& nullNode){
brTreeNode<K,E>*s = p->leftChild,*ps = p,*gs=pp;//初始定义祖父,父节点
while (s->rightChild != &nullNode){//寻找替换节点
if(s->color=='b')
eraseAdjust(gs,ps,s);
gs=ps;
ps = s;
s = s->rightChild;
}
p->element.first=s->element.first;
p->element.second=s->element.second;
//删除红色
if(s->color=='r'){
delete s;
if(ps==p)
ps->leftChild=&nullNode;
else
ps->rightChild=s->leftChild;
}
//2删除黑色(有一个红色的左孩子)
else{
if(ps==p)
{ps->leftChild=s->leftChild;
ps->leftChild->color='b';
}
else
{ps->rightChild=s->leftChild;
ps->rightChild->color='b';
}
delete s;
}
}
else{
if(p->color=='b')
{
if(p->element.first>pp->element.first)
{
p->rightChild->color='b';
pp->rightChild=p->rightChild;
}
else
{
pp->leftChild=p->rightChild;
pp->leftChild->color='b';
}
}
else
{
if(p->element.first>pp->element.first)
pp->rightChild=&nullNode;
else
pp->leftChild=&nullNode;
}
delete p;
}
}
删除示例
1、删除90
2、删除80
3、删除70
4、删除65
红黑树的插入及一些基础函数请见https://blog.youkuaiyun.com/weixin_41533852/article/details/96877592