构建
红黑树是一个较平衡二叉树,最深的叶子节点最多不能超过最浅的叶子节点的深度二倍
具有性质:
- 节点是红色或黑色。
- 根节点是黑色。
- 所有叶子都是黑色。(叶子是NUIL节点)
- 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
- 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点
对于结点的信息有:该节点的颜色、左右儿子指针、该节点储存的数据
#define RED 1
#define BLACK 0
template <typename T>
class RBTNode{
public:
T data;
int color;
RBTNode*l,*r;
RBTNode(T data){
this.data=data;
color=RED;
l=r=NULL;
}
};
对于红黑树的操作:主要 插入、查找、删除
template <typename T> class RBT{
private:
RBTNode<T> *Root;
void getColor(RBTNode<T> *root);
void insert(RBTNode<T> *&root,T data);
RBTNode<T> * find(RBTNode<T> *root,T data);
void clear(RBTNode<T> *root);
public:
void insert(T data);
void remove(T data);
bool find(T data);
void clear();
RBT();
~RBT();
};
template <typename T>
RBT<T>::RBT(){
root=NULL;
}
template <typename T>
RBT<T>::~RBT(){
clear();
}
插入
插入的过程就想 普通的二叉搜索树一样插入
插入一个节点就是直接插入一个红色叶子。
插入之后可能会破坏红黑树的 第一条性质和第四条性质。
当出现之后 可以通过 变色和旋转使得再次恢复红黑树的五条性质
插入时需要旋转和变色的情况具体的分可以分为8种
对于每一中情况的旋转变色方案的解决方法
对于前四种不需要旋转,只需要对第一层第二层变色既可以恢复其第四条性质
然后递归到上一层在解决
对于后四种情况,既需要选择也需要变色,其前后变化关系从图中可用看出
template <typename T>
void RBT<T>::getColor(RBTNode<T> *root){
return root==NULL?BLACK:root->color;
}
template <typename T>
void RBT<T>::insert(RBTNode<T> *&root,T data){
if(root==NULL)root=new RBTNode<T>(data);
else insert(data<root->data?root->l:root->r,data);
if(getColor(root->l)==BLACK&&getColor(root->r)==BLACK)return;
if(getColor(root->l)==RED&&getColor(root->r)==RED){
root->l->color=root->r->color=BLACK;
root->color=RED;
}
if(getColor(root->l)==RED&&getColor(root->l->l)==RED){
root->l->l=BLACK;
RBTNode<T> * lson= root->l;
root->l=lson->r;
lson->r=root;
root=lson;
}
if(getColor(root->l)==RED&&getColor(root->l->r)==RED){
root->l=BLACK;
RBTNode<T> * lrson= root->l->r;
root->l->r=lrson->l;
lrson->l=root->l;
root->l=lrson->r;
lrson->r=root;
root=lrson;
}
if(getColor(root->r)==RED&&getColor(root->l->l)==RED){
root->r=BLACK;
RBTNode<T> * rlson= root->r->l;
root->r->l=rlson->r;
rlson->r=root->r;
root->r=rlson->l;
rlson->l=root;
root=rlson;
}
if(getColor(root->r)==RED&&getColor(root->l->r)==RED){
root->r->r=BLACK;
RBTNode<T> * rson= root->r;
root->r=rson->l;
rson->l=root;
root=rson;
}
}
template <typename T>
void RBT<T>::insert(T data){
insert(Root,data);
Root->color=BLACK;
}
查找
template <typename T>
RBTNode<T> * RBT<T>::find(RBTNode<T> *root,T data){
if(root==NULL)return NULL;
if(root->data==data)return root;
return data<root->data?find(root->l,data):find(root->r,data);
}
template <typename T>
bool RBT<T>::find(T data){
return find(Root,data)!=NULL;
}
//clear 顺便直接加上
template <typename T>
void RBT<T>::clear(RBTNode<T> *root){
if(root==NULL)return;
clear(root->l);
clear(root->r);
delete root;
}
template <typename T>
void RBT<T>::clear(){
clear(Root);
}
删除
删除的情况讨论比较多
- 删除的节点是一个叶子节点
- 删除的节点拥有一个儿子节点
- 删除的节点拥有两个儿子节点
删除节点是叶子节点
如果删除节点是红色的话:
直接删掉就好。
如果删除节点为黑色
-
兄弟节点为红色
-
兄弟节点的儿子节点为红色
-
父节点为红色
-
兄弟节点,兄弟节点儿子节点,父亲节点都是黑色
删除节点拥有一个儿子节点
如果只有一个儿子,显然那个儿子不能为黑色,而且是个叶子节点
如果为黑色或者拥有其他儿子节点,那么久不可能满足 上面的性质5。
所以,如果被删除节点有一个儿子,那么那个儿子节点一定为红色叶子节点。
所以删除操作很简单,就是用那个叶子节点代替我们被删除的节点就可以。
删除节点拥有两个儿子节点
寻找该节点的前继(一定是一个叶子节点),删除掉前继,转化为第一种情况,然后使用被删除前继的值,代替当前节点的值。