在解决A*寻路算法时,需要一个能支持大量插入和删除(查找)操作的数据结构
链表和数组都有缺陷,这时就想到了树,将树设置为左子树小于自身,右子树大于自身,这时查找,插入,删除都比较快捷,但是如果插入数据特例例如递增的数据
就会导致效率比链表还差
理所当然想到自我平衡的树 AVL树和红黑树(没看到2-3树)
然后再思索了一下,发现维护AVL树的代价比较高,感觉到插入的时候,旋转AVL数的次数较多,于是旋转了红黑树。
//参考书籍:算法导论
红黑树的定义
红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
性质1. 节点是红色或黑色。
性质2. 根节点是黑色。
性质3 每个叶节点(NIL节点,空节点)是黑色的。
性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
红黑树的插入:首先 插入的数据必然是叶子节点(原因:只要该节点不为空,则往左右子树中查找,最后结束必然是在叶子节点)
最简单二叉树的插入(不是红黑树) //部分代码是在编辑页敲的,可能有问题
tree* new(int i)
{
tree *now = NULL;
now = (tree*)malloc(sizeof(tree));
memset(now,0,sizeof(tree));
now->date = i;
now->leftchild = p.nil;
now->rightchild = p.nil;
return now;}
void Insert(root_nil p, int i)
{
if(NULL == now)//为空树
{
p.root = new(i);
}
tree *now = p.root;
tree *parent = now;
while(now != p.nil)
{
parent = now;
if(now->date() < i)
{
now = now->rightchild;
}else
{
now = now->leftchild;
}
}
now = new(i);
now->parent = parent;
if(parent->date() < i)
{
parent->rightchild = now;
}else
{
parent->leftchild = now;
}
}
然后是插入,要保证黑色节点不变 所以将插入的节点都先设置为红色 //有可能破坏条件4,但其他条件不影响
分类讨论 插入节点的父节点的颜色
若父节点是黑色,此红黑树的平衡不改变
若父节点是红色则需要进行操作
//以下讨论皆基于父节点是红色
//(父节点是红色)插入的节点是叶子节点,假设存在兄弟节点 根据红黑树的定义 兄弟节点必然为黑色,为了满足红黑树条件5,那么兄弟节点必然为哨兵(nil)
//那么兄弟节点不存在(反证法)尽量不要对nil进行操作,所以选择去寻找叔节点的颜色
//情况1之后可能会出现兄弟节点存在并且为黑色 这时其实就是情况2所以讨论谁都是一样的
因为父节点是红色所以爷爷节点必然为黑色,叔节点的颜色未知
继续分类讨论
若叔节点的颜色为红 :此时条件为 父红 爷黑 叔红 情况1
此时若将爷节点的黑色下移给父节点和叔节点,黑色节点个数还是稳定,但是要考虑爷节点的父节点有可能是红色
所以 z = z->parent->parent; 然后继续循环
叔节点为黑:此时为父红 爷黑 叔黑 并且子节点是父节点的右孩子情况2
左旋 然后变成情况3
此时为父红 爷黑 叔黑 并且子节点是父节点的左孩子情况3
总结:绘制状态图
然后是红黑树的删除
要理解红黑树的删除,首先要理解二叉树的删除(请关注ββ 以及 13的移动)
红黑树删除时也是这样做的
只是根据删除的节点颜色以及替换节点的颜色然后再保持平衡
//删除节点为红 (删红) 替换节点为红(替红)
可以分为四种情况
1.删红替红 //不影响树我将数值替换,然后删除替换节点
2.删红替黑 //我将数值替换,然后删除替换节点
3.删黑替红 //我如果只是将数值替换 ,然后删除替换节点即可
4.删黑替黑 //我将数值替换,然后删除替换节点
可以发现1,3情况树的平衡不发生改变2,4情况 树的替换节点以下少了一个黑色节点
我选择在右子树中寻找最小值
rebtree* Tree_MiniMum(root_nil T,rebtree* x)
{
while(x->leftchild != T.nil)
{
x = x->leftchild;
}
return x;
}
//2,4情况下 替换节点都是黑色
//然后讨论其父兄节点 ββ区域少了一个黑色节点
父黑兄黑
父红兄红 //此情况不可能存在 原本是红黑树
父黑兄红
父红兄黑
1父黑兄红
2.父黑兄黑
//完全无从下手,那就去看看兄弟的孩子节点 nil->rightchild = NULL nil->leftchild = NULL nil->color = Black
2.1 w.rightchild == Black && w.leftchild == Black
突然发现只要兄弟节点的左右孩子都为黑色,那么无论父节点是什么颜色效果一样
即父颜色随意兄黑兄的左右孩子都为黑,那么可以将该状态上移(ββ上移)
2.2然后兄弟节点的右孩子为黑,左孩子是红时
依然发现只要兄弟节点的右孩子为黑,左孩子是红时,那么无论父节点是什么颜色效果一样,现在的情况就是2.3状态
2.3兄弟节点的右孩子红时
至此讨论结束,所有情况完结
总结:
删除状态图
模板类代码
#ifndef RBTREE_H
#define RBTREE_H
#include <stdio.h>
#include <stdlib.h>
enum ColorType
{
Red = 0,
Black
};
template<class T> class rbt_root_nil;
template<class T>
class rbtree{
public:
rbtree(rbt_root_nil<line> *p);
void SetDate(T Date);
public:
ColorType color;
rbtree<T> *rightchild;
rbtree<T> *leftchild;
rbtree<T> *parent;
T date;
};
template<class T>
class rbt_root_nil
{
public:
rbt_root_nil();
~rbt_root_nil();
rbtree<T>* newRbtreeNode(T i);
rbtree<T>* FindMin();
void LeftRotate(rbtree<T> *x);
void RightRotate(rbtree<T> *y);
void RB_InsertFixup(rbtree<T> *nowNode);
void Insertrbtree(T i);
void Insertrbtree(rbtree<T> *nowNode);
void Rb_TransPlant(rbtree<T> *u,rbtree<T> *v);
rbtree<T>* Tree_MiniMum(rbtree<T>* x);
void Rb_Delete_Fixup(rbtree<T>* x);
rbtree<T>* FindRBtreeByNum(T delnum);
void Deleterbtree(rbtree<T>* z);
void Deleter(T delnum);
void rbt_root_nil<T>::DelNotFree(rbtree<T>* z);
public:
rbtree<T>* root;
rbtree<T>* nil;
};
template<class T>
rbtree<T>::rbtree(rbt_root_nil<line> *p)
{
color = Red;
rightchild = p->nil;
leftchild = p->nil;
parent = p->nil;
}
template<class T>
void rbtree<T>::SetDate(T Date)
{
date = Date;
}
//编译不通过
/*
template<class T>
rbtree<T>::rbtree(T &t)
{
color = Red;
rightchild = nil;
leftchild = nil;
parent = nil;
date = t;
}*/
template<class T>
rbt_root_nil<T>::rbt_root_nil()
{
nil = (rbtree<T>*)malloc(sizeof(rbtree<T>));
if(NULL == nil)
{
return;
}
nil->color = Black;
nil->leftchild = NULL;
nil->rightchild = NULL;
nil->parent = NULL; //所有未满节点都是哨兵的父节点,所以将哨兵父节点设为空
root = nil; //存在不添加就结束的可能性
return ;
}
template<class T>
rbt_root_nil<T>::~rbt_root_nil()
{
//释放内存
if(root != NULL && root != nil) //先判断二叉树是否为空为空这root == nil 时两个指针指向同一个内存空间只能释放一次
{
delete(root);
root = NULL;
}
if(nil != NULL)
{
delete(nil);
nil = NULL;
}
return ;
}
template<class T>
rbtree<T>* rbt_root_nil<T>::newRbtreeNode(T i)
{
rbtree<T>* use = (rbtree<T>*)malloc(sizeof(rbtree<T>));
if(use == NULL)
{
return NULL;
}
use->date = i;
use->color = Red;
return use;
}
template<class T>
void rbt_root_nil<T>::LeftRotate(rbtree<T> *x)
{
rbtree<T>* y = x->rightchild;
x->rightchild = y->leftchild;
if(y->leftchild != nil)
{
y->leftchild->parent = x;
}
y->parent = x->parent;
if(x->parent == nil)
{
root = y;
}else
{
if(x == x->parent->leftchild)
{
x->parent->leftchild = y;
}else
{
x->parent->rightchild = y;
}
}
y->leftchild = x;
x->parent = y;
}
template<class T>
void rbt_root_nil<T>::RightRotate(rbtree<T> *y)
{
rbtree<T>* x = y->leftchild;
y->leftchild = x->rightchild;
if(x->rightchild != nil)
{
x->rightchild->parent = y;
}
x->parent = y->parent;
if(y->parent == nil) //如果y是根节点,旋转后x为根节点
{
root = x;
}else
{
if(y == y->parent->rightchild) //不然就根据左右孩子来讲x接上
{
y->parent->rightchild = x;
}else
{
y->parent->leftchild = x;
}
}
x->rightchild = y;
y->parent = x;
}
template<class T>
void rbt_root_nil<T>::RB_InsertFixup(rbtree<T> *nowNode)
{
rbtree<T>* use = NULL;
while(Red == nowNode->parent->color) //父节点为红色,当前节点本就是红色,不符合红黑树,所以进行修改
{
if(nowNode->parent == nowNode->parent->parent->leftchild) //判断父节点是否是爷爷节点的左孩子
{
use = nowNode->parent->parent->rightchild;
if(use->color == Red) //如果父节点和叔节点都是红色节点,那么直接把爷爷黑色节点下移,保证下部分的节点符合红黑树
{
nowNode->parent->color = Black; //父节点变色
use->color = Black; //叔节点变色
nowNode->parent->parent->color = Red; //爷爷节点变色 //此时可能出现爷爷节点和爷爷的父节点都是红色,所以再循环时,将爷爷节点设置为当前节点
nowNode = nowNode->parent->parent;
}else //父节点为红,叔节点为黑 发生在变色过后 将右边多的一个黑色变成根节点然后将原节点变色后往另半棵树移动
{
if (nowNode == nowNode->parent->rightchild) //判断当前节点是否是父节点的右孩子
{
nowNode = nowNode->parent;
LeftRotate(nowNode); //直接将父节点进行左旋
}
nowNode->parent->color = Black;
nowNode->parent->parent->color = Red;
RightRotate(nowNode->parent->parent);
}
}
else//父节点是否是爷爷节点的右孩子
{
use = nowNode->parent->parent->leftchild;
if(use->color == Red) //如果父节点和叔节点都是红色节点,那么直接把爷爷黑色节点下移,保证下部分的节点符合红黑树
{
nowNode->parent->color = Black; //父节点变色
use->color = Black; //叔节点变色
nowNode->parent->parent->color = Red; //爷爷节点变色 //此时可能出现爷爷节点和爷爷的父节点都是红色,所以再循环时,将爷爷节点设置为当前节点
nowNode = nowNode->parent->parent;
}else //父节点为红,叔节点为黑 此时叔节点为哨兵
{
if (nowNode == nowNode->parent->leftchild) //判断当前节点是否是父节点的右孩子
{
nowNode = nowNode->parent;
RightRotate(nowNode); //直接将父节点进行右旋
}
nowNode->parent->color = Black;
nowNode->parent->parent->color = Red;
LeftRotate(nowNode->parent->parent); //
}
}
}
root->color = Black;
}
template<class T>
void rbt_root_nil<T>::Insertrbtree(rbtree<T> *nowNode)
{
rbtree<T>* y = nil;
rbtree<T>* x = root;
while(x != nil) //从根开始寻找,插入永远是插入树的叶子节点
{
y = x;
if(nowNode->date < x->date)
{
x = x->leftchild;
}else
{
x = x->rightchild;
}
}
nowNode->parent = y;
if(y == nil)
{
root = nowNode; //如果root == NULL 将现在设置为根
}else
{
if(nowNode->date < y->date) //根据与当前叶子节点的对比获得所在位置
{
y->leftchild = nowNode;
}else
{
y->rightchild = nowNode;
}
}
nowNode->leftchild = nil; //设定左右孩子
nowNode->rightchild = nil;
RB_InsertFixup(nowNode); //插入后决定是否要变更树
}
template<class T>
void rbt_root_nil<T>::Insertrbtree(T i)
{
rbtree<T>* nowNode = newRbtreeNode(i);
Insertrbtree(nowNode);
}
template<class T>
void rbt_root_nil<T>::Rb_TransPlant(rbtree<T> *u,rbtree<T> *v)
{
if(u->parent == nil)
{
root = v;
}else if (u == u->parent->leftchild)
{
u->parent->leftchild = v;
}else
u->parent->rightchild = v;
v->parent = u->parent;
}
template<class T>
rbtree<T>* rbt_root_nil<T>::Tree_MiniMum(rbtree<T>* x)
{
while(x->leftchild != nil)
{
x = x->leftchild;
}
return x;
}
template<class T>
void rbt_root_nil<T>::Rb_Delete_Fixup(rbtree<T>* x)
{
rbtree<T>* w = NULL;
while(x != root && x->color == Black)
{
if(x == x->parent->leftchild)
{
w = x->parent->rightchild;
if(w->color == Red)
{
w->color = Black;
x->parent->color = Red;
LeftRotate(x->parent);
w = x->parent->rightchild;
}
//
if(w == nil)
{
printf("error");
}
if(w->leftchild->color == Black && w->rightchild->color == Black)
{
w->color = Red;
x = x->parent;
}else
{
if (w->rightchild->color == Black)
{
w->leftchild->color = Black;
w->color = Red;
RightRotate(w);
w = x->parent->rightchild;
}
w->color = x->parent->color;
x->parent->color = Black;
w->rightchild->color = Black;
LeftRotate(x->parent);
x = root;
}
}
else
{
w = x->parent->leftchild;
//printf("%d",w->key);
if(w->color == Red)
{
w->color = Black;
x->parent->color = Red;
RightRotate(x->parent);
w = x->parent->leftchild;
}
if(w->leftchild->color == Black && w->rightchild->color == Black)
{
w->color = Red;
x = x->parent;
}else
{
if (w->leftchild->color == Black)
{
w->rightchild->color = Black;
w->color = Red;
LeftRotate(w);
w = x->parent->leftchild;
}
w->color = x->parent->color;
x->parent->color = Black;
w->leftchild->color = Black;
RightRotate(x->parent);
x = root;
}
}
}
x->color = Black;
}
template<class T>
rbtree<T>* rbt_root_nil<T>::FindRBtreeByNum(T delnum)
{
rbtree<T>* y = nil;
rbtree<T>* x = root;
while(x != nil)
{
y = x;
if(delnum == x->date)
{
break;
}
if(delnum < x->date)
{
x = x->leftchild;
}else
{
x = x->rightchild;
}
}
if(y == nil) //该元素不存在
{
return NULL;
}
return y;
}
template<class T>
void rbt_root_nil<T>::Deleterbtree(rbtree<T>* z)
{
if(z == NULL)
{
return ;
}
rbtree<T>* y = z;
rbtree<T>* x;
ColorType yOriginalColor = y->color;
if(z->leftchild == nil)
{
x = z->rightchild;
Rb_TransPlant(z,z->rightchild);
}else
{
if(z->rightchild == nil)
{
x = z->leftchild;
Rb_TransPlant(z,z->leftchild);
}
else
{
y = Tree_MiniMum(z->rightchild);
yOriginalColor = y->color;
x = y->rightchild;
if(y->parent == z)
{
x->parent = y;
}
else
{
Rb_TransPlant(y,y->rightchild);
y->rightchild = z->rightchild;
y->rightchild->parent = y;
}
Rb_TransPlant(z,y);
y->leftchild = z->leftchild;
y->leftchild->parent = y;
y->color = z->color;
}
}
if(yOriginalColor == Black)
{
Rb_Delete_Fixup(x);
}
}
template<class T>
void rbt_root_nil<T>::Deleter(T delnum)
{
rbtree<T>* z = FindRBtreeByNum(delnum);
Deleterbtree(z);
free(z);
}
template<class T>
void rbt_root_nil<T>::DelNotFree(rbtree<T>* z)
{
Deleterbtree(z); //移除但不释放内存
}
template <class T>
rbtree<T>* rbt_root_nil<T>::FindMin()
{
return Tree_MiniMum(root);
}
#endif // rbtree