基于c++的红黑树的插入与删除代码
红黑树的特点
红黑树的特性:
-
根节点为黑色节点
-
外部节点(空节点)为黑色节点
-
节点要么为黑色要么为红色
-
不能出现两个连续的红色节点
-
任意一个根节点到外部节点的路径上所经历的黑色节点个数相同(即对于黑色节点为绝对平衡!)
对于红黑树的插入操作使插入节点默认为红色从而不影响黑高的变化也就是不打破最后一个特性,因此需要每次检查是否出现两个连续的红色节点。
对于红黑树的删除操作则需要每次检查对于黑色节点是否为绝对平衡。
而基础操作左旋右旋与AVL树中操作基本相同,不同点在于比如左旋是将待旋转节点作为其右孩子节点的左孩子节点,将原右孩子节点的左孩子节点过继到待旋节点的右孩子下,同时交换待旋节点和其右孩子节点的颜色。
总的来说:
-
删除操作需要判断被删除节点的的兄弟节点的颜色以及父节点的颜色来判断是否违反红黑树的特性
-
插入操作根据判断插入节点的父节点的颜色,以及父节点的兄弟节点也就是叔叔节点的颜色来判断是否违反红黑树的特性
红黑树的插入操作
插入操作的具体步骤这里省略,只写出整个插入的可能情况以及整个编码思路
-
根据上文插入节点的颜色默认为红色
-
若被插入节点的父节点为黑色则不违反红黑树的特性,可以继续插入,而若被插入节点的父节点为红色则违反不能出现两个连续的红色节点的特性因此需要对整个二叉树进行调整。
-
这里以被插入节点的父节点为其爷爷节点的左节点为例,右节点的情况完全类似!
-
两个bool量new_insert和obey当为true时分别表示新插入了一个节点以及违反了出现两个连续的红色节点的特性!
据此解释出现两个连续红色节点的情况(也就是x和p同为红色节点,x可为p的左孩子节点也可为p的右孩子节点,各位可以在纸上跟着思路画出来会对理解很有帮助):
假定被插入节点为x,x的父节点为p,p的父节点也就是x的爷爷几点为g,x的叔叔节点为u
情况1:当u为红色节点时候,此时情况较为简单,无论x是p的左孩子还是右孩子节点,只需要将p和u变为黑色,同时判断g是否为根节点,如若是则保持原黑色,如若不是则将其转换为红色(也就是将g作为新的x往回递推检查),继续检查g的父节点是否为红色从而往回递推!
情况2:当u为黑色节点的时候,这里注意当然u可以为空!!!!分为两种情况,最后将new_insert和obey设为false
情况2.1:x为p的右孩子节点,此时需要先左旋p,然后再右旋g
情况2.2:x为p的左孩子节点,此时只需要右旋g即可
至此插入操作已完成,示意图如下:
红黑树的删除操作
红黑树的删除操作情况多且麻烦,在最初同AVL树一样,先分析删除的节点是1.叶子节点 2.只有左节点或右节点 3.既有左节点又有右节点 的三种情况。
-
首先在情况1中若叶子节点是红色,那么直接删除即可,若是黑色节点则需要判断其兄弟节点以及其父节点的颜色判断,最为复杂!
-
其次在情况2中若待删除节点只有左或者右子树(由红黑树特性可知该节点必为黑色节点且左或者右子树必为红色叶节点),这种情况简单只需要红黑节点data互换,然后直接删除红色叶节点即可。
-
最后在情况3中,同二叉搜索树删除既有左右子树的节点一样,用最接近他的值的节点替换然后转换到情况1或情况2,删除替换他的节点即可。
-
其中变量blackH_change为true时候用来指示黑高改变,即需要调整红黑树
下面以被删除节点为黑色叶节点,且在其父节点的左子树下为例说明各种情况变换,此时兄弟节点即为父节点的右孩子节点下面称之为右兄,在其父节点右子树下同理对称转换即可。
代码展示
红黑树头文件如下,这里没有采用类模板默认data类型为int,且插入删除没有返回bool以及类似STL一样的指向其节点的指针(迭代器)。
#pragma once
#include<iostream>
#include<vector>
using namespace std;
enum Color
{
red,
black
};
struct RBNode{
RBNode(int d,Color c):data(d),color(c),lchild(nullptr),rchild(nullptr){}
~RBNode(){
delete lchild;
delete rchild;
}
int data;
Color color;
RBNode *lchild;
RBNode *rchild;
};
class RBTree{
public:
RBTree(const vector<int> &);
void insert(int);
void MidOrderTraversal() const;
void DeleteNode(int);
private:
RBNode *head;
bool obey;//表示被插入节点的父节点是个红色节点即违反不能连续两个红色节点的条件
bool newInsert;//表示新插入了一个节点,用来判断被插入节点的父节点是红黑从而判断是否违反不能连续两个红色节点的条件
bool blackH_change;//表示删除过程中黑高改变
void R_Rotate(RBNode *&);
void L_Rotate(RBNode *&);
void insert(RBNode *&, int);
void LeftLoseToBalance(RBNode *&);
void RightLoseToBalance(RBNode *&);
void MidOrderTraversal(RBNode *) const;
void DeleteNode(RBNode *&, int);
};
RBTree::RBTree(const vector<int> &v):head(nullptr),obey(false),newInsert(false),blackH_change(false)
{
for (const auto &i : v)
{
if (!head)
head = new RBNode(i, black);
else
insert(head, i);
}
}
void RBTree::R_Rotate(RBNode *&rp)
{
auto l = rp->lchild;
Color tem_color = rp->color;
rp->color = l->color;
l->color = tem_color;
rp->lchild = l->rchild;
l->rchild = rp;
rp = l;
}
void RBTree::L_Rotate(RBNode *&rp)
{
auto r = rp->rchild;
Color tem_color = rp->color;
rp->color = r->color;
r->color = tem_color;
rp->rchild = r->lchild;
r->lchild = rp;
rp = r;
}
//左面失去平衡说明总体需要右旋,说明该节点的左子树出现两个连续的红色节点,需要判断其左红色节点的下一个红色节点是其右孩子节点还是左孩子节点
void RBTree::LeftLoseToBalance(RBNode *&rp)
{
auto l = rp->lchild;
if (l->lchild && l->lchild->color == red)
R_Rotate(rp);
else
{
L_Rotate(rp->lchild);
R_Rotate(rp);
}
}
//右面失去平衡同理
void RBTree::RightLoseToBalance(RBNode *&rp)
{
auto r = rp->rchild;
if (r->rchild && r->rchild->color == red)
L_Rotate(rp);
else
{
R_Rotate(rp->rchild);
L_Rotate(rp);
}
}
void RBTree::insert(RBNode *&rp, int key)
{
if (rp->data > key)
{
if (!rp->lchild)
{
rp->lchild = new RBNode(key, red);
newInsert = true;
}
else
insert(rp->lchild, key);
if (obey)
{
if (rp->rchild && rp->rchild->color == red)
{
rp->lchild->color = rp->rchild->color = black;
if (rp != head)
{
rp->color = red;
newInsert = true;
obey = false;
return;
}
else
obey = newInsert = false;
}
else
{
LeftLoseToBalance(rp);
obey = newInsert = false;
}
}
if (newInsert)
{
if (rp->color == red)
obey = true;
else
obey = newInsert = false;
}
}
else
{
if (!rp->rchild)
{
rp->rchild = new RBNode(key, red);
newInsert = true;
}
else
insert(rp->rchild, key);
if (obey)
{
if (rp->lchild && rp->lchild->color == red)
{
rp->lchild->color = rp->rchild->color = black;
if (rp != head)
{
rp->color = red;
newInsert = true;
obey = false;
return;//避免该节点重复判断!
}
else
obey = newInsert = false;
}
else
{
RightLoseToBalance(rp);
newInsert = obey = false;
}
}
if (newInsert)
{
if (rp->color == red)
obey = true;
else
obey = newInsert = false;
}
}
}
void RBTree::insert(int key)
{
insert(head, key);
}
void RBTree::MidOrderTraversal() const
{
MidOrderTraversal(head);
cout << endl;
}
void RBTree::MidOrderTraversal(RBNode *p) const
{
if(!p)
return;
else
{
MidOrderTraversal(p->lchild);
cout<<p->data<<" ";
MidOrderTraversal(p->rchild);
}
}
void RBTree::DeleteNode(int key)
{
DeleteNode(head, key);
}
void RBTree::DeleteNode(RBNode *&rp, int key)//rp==reference to pointer
{
if (rp->data == key)
{
if (!rp->rchild && !rp->lchild)
{
if (rp->color == red)//被删除的节点为红色叶节点,直接删除即可,黑高不改变
{
auto ready = rp;
rp = nullptr;
delete ready;
blackH_change = false;
}
else //被删除的节点为黑色叶节点,删除后将黑高改变指示变为true用于往前递推调整红黑树至平衡
{
auto ready = rp;
rp = nullptr;
delete ready;
blackH_change = true;
}
}
//只有左孩子或右孩子则该节点必为黑色节点且左右孩子节点必为红色节点,把值对应替换然后正常删除红色孩子节点即可
else if (!rp->rchild)
{
auto ready = rp->lchild;
rp->data = ready->data;
rp->lchild = nullptr;
delete ready;
blackH_change = false;
}
else if (!rp->lchild)
{
auto ready = rp->rchild;
rp->data = ready->data;
rp->rchild = nullptr;
delete ready;
blackH_change = false;
}
//既有左孩子又有右孩子,用节点左子树最大的节点替换,然后重新遍历删除被替换的节点,随后将树高指示设为false,因此往回递推过程中
//不会再重新判断红黑树的平衡性
else
{
auto l = rp->lchild;
while (l->rchild)
{
l = l->rchild;
}
auto tep = l->data;
DeleteNode(head, tep);
blackH_change = false;
rp->data = tep;
}
}
else if (rp->data > key)
{
DeleteNode(rp->lchild, key);
if(blackH_change)
{
auto s = rp->rchild;
if (s->color == black) //兄弟节点是黑色
{
if (!s->lchild && !s->rchild)//兄弟节点为空
{
if (rp->color == red)//如果父节点为红色,则p和s调换颜色即可
{
rp->color = black;
s->color = red;
blackH_change = false;
}
else //如果父节点为黑色,则s设置红色,将p作为新的破坏红黑树特性的节点,往回遍历!!!!
{
s->color = red;
blackH_change = (rp == head) ? false : true;
}
}
else
{
if (s->lchild && s->lchild->color == red)//兄弟节点左孩子存在且左孩子为红色
{
R_Rotate(rp->rchild);
L_Rotate(rp);
rp->rchild->color = black;
blackH_change = false;
}
//兄弟节点右孩子存在且右孩子为红色 或者是 左右都不为空且左右都为红色
else if ((s->rchild && s->rchild->color == red) || (s->lchild && s->rchild && s->lchild->color ==red && s->rchild->color == red))
{
L_Rotate(rp);
rp->rchild->color = black;
blackH_change = false;
}
//兄弟节点左右都不为空且左右都为黑色
else if (s->lchild && s->rchild && s->lchild->color == black && s->rchild->color == black)
{
if (rp->color == red)//如果父节点为红色 即红黑树得到平衡
{
s->color = red;
rp->color = black;
blackH_change = false;
}
else if (rp == head)
{
s->color = red;
blackH_change = false;
}
else
{
s->color = red;
blackH_change = true;
}
}
}
}
else //兄弟节点为红色
{
L_Rotate(rp);
auto new_s = rp->lchild->rchild;
//如果新兄弟节点左右为空或者左右均为黑色
if ((!new_s->lchild && !new_s->rchild)|| (new_s->lchild && new_s->rchild && new_s->rchild->color==black && new_s->lchild->color==black))
{
rp->lchild->color = black;
new_s->color = red;
blackH_change = false;
}
//最多旋转三次
else if (new_s->lchild && new_s->lchild->color==red)
{
R_Rotate(rp->lchild->rchild);
L_Rotate(rp->lchild);
rp->lchild->rchild->color = black;
blackH_change = false;
}
else if ((new_s->rchild && new_s->rchild->color==red) || (new_s->lchild && new_s->rchild && new_s->rchild->color==red && new_s->lchild->color==red))
{
L_Rotate(rp->lchild);
rp->lchild->rchild->color = black;
blackH_change = false;
}
}
}
}
else
{
DeleteNode(rp->rchild, key);
if (blackH_change)
{
auto s = rp->lchild;
if (s->color == black)
{
if (!s->lchild && !s->rchild)
{
if (rp->color == red)
{
rp->color = black;
s->color = red;
blackH_change = false;
}
else
{
s->color = red;
blackH_change = (rp == head) ? false : true;
}
}
else
{
if (s->rchild && s->rchild->color == red)
{
L_Rotate(rp->lchild);
R_Rotate(rp);
rp->lchild->color = black;
blackH_change = false;
}
else if ((s->lchild && s->lchild->color == red) || (s->lchild && s->rchild && s->lchild->color ==red && s->rchild->color == red))
{
R_Rotate(rp);
rp->lchild->color = black;
blackH_change = false;
}
else if (s->lchild && s->rchild && s->lchild->color == black && s->rchild->color == black)
{
if (rp->color == red)
{
s->color = red;
rp->color = black;
blackH_change = false;
}
else if (rp == head)
{
s->color = red;
blackH_change = false;
}
else
{
s->color = red;
blackH_change = true;
}
}
}
}
else
{
R_Rotate(rp);
auto new_s = rp->rchild->lchild;
if ((!new_s->lchild && !new_s->rchild)|| (new_s->lchild && new_s->rchild && new_s->rchild->color==black && new_s->lchild->color==black))
{
rp->rchild->color = black;
new_s->color = red;
blackH_change = false;
}
//最多旋转三次
else if (new_s->rchild && new_s->rchild->color==red)
{
L_Rotate(rp->rchild->lchild);
R_Rotate(rp->rchild);
rp->rchild->lchild->color = black;
blackH_change = false;
}
else if((new_s->lchild && new_s->lchild->color==red) || (s->lchild && s->rchild && s->lchild->color ==red && s->rchild->color == red) )
{
R_Rotate(rp->rchild);
rp->rchild->lchild->color = black;
blackH_change = false;
}
}
}
}
}
测试用例如下:
#include"RedBlackTree.h"
//生成100000个不重复的随机数用来初始化红黑树,然后将数字排序删除到只剩最后10个数字,最终输出最后10个有序数字
int main()
{
vector<int> temp;
for (int i = 0; i < 100000; ++i)
{
temp.push_back(i + 1);
}
random_shuffle(temp.begin(), temp.end());
int j = temp.size();
RBTree t(temp);
sort(temp.begin(), temp.end());
for (int i = 0; i < j-10;++i)
{
t.DeleteNode(temp[i]);
}
t.MidOrderTraversal();
}
最后输出:
99991 99992 99993 99994 99995 99996 99997 99998 99999 100000