红黑树
红黑树是一种二叉查找树。
一棵二叉查找树若满足如下性质,则为红黑树:
1)每个节点是黑的或是红的;
2)根节点是黑的;
3)每个叶节点是黑的;
4)如果一个节点是黑的,则它的两个孩子都是黑的。
5)对每个节点,从该节点到其子孙节点的路径(一直到叶节点)上包含相同数目的黑节点。
注:叶节点的关键字为空,用NIL表示
黑高度:从某个节点(不包括该节点)到达一个叶节点的任意一条路径上的黑色节点的个数。
定理:一棵有n个内节点的红黑树的高度至多为2lg(n+1).
证明:将红黑树转化为2-3-4树,转化过程如下图所示:
假设红黑树中内节点个数为n,高度为h,2-3-4树高度为h1,
在2-3-4树中,
2h1≤#leaves(叶节点个数)≤4h1
2
h
1
≤
#
l
e
a
v
e
s
(
叶
节
点
个
数
)
≤
4
h
1
(
∵
∵
单个节点个数大于等于2而小于等于4)
根据定理:一棵平衡树的叶节点数目等于内部节点数+1,所以:
2h1≤n+1≤4h1
2
h
1
≤
n
+
1
≤
4
h
1
⟹h≤lg(n+1)
⟹
h
≤
l
g
(
n
+
1
)
,因为红黑树中在一条路径红色节点个数最多占总节点个数一半,所以
h≤2h1
h
≤
2
h
1
,
⟹h≤2lg(n+1)
⟹
h
≤
2
l
g
(
n
+
1
)
插入
首先找到要插入的位置将其插入(插入节点z时z被着为红色,key[z] = k),这一步和普通二叉查找树一样,然后,为了保持红黑树的性质,需要调用一个辅助程序RBInsertFixup(T,k)来对节点重新着色并旋转.。时间为O(lgn).
z要设为红色的原因:为了保持性质5,使所有节点黑高度相同。
插入之后,如果p[z]为红色,就违反了性质4:不能出现连续的红色结点。所以RBInsertFixup(T,z)的目的就是纠正这个错误,其中while循环成立的条件即color[p[z]]=RED.当树中只有一个节点的时候,就违反了性质2:根节点是黑的,这个很好解决,主要解决性质4的问题。
RBInsertFixup(T,k)需要处理三种情况:
1)z的叔叔y是红色的:此时可以将p[z]和y都着为黑色已解决z和p[z]都是红色的问题,并将p[p[z]]着为红色(保持黑高度不变),然后将p[p[z]]作为新的z来重复循环
2)z的叔叔y是黑色的,而且z是右孩子:对z的父亲进行左旋,使其变为情况三
3)z的叔叔y是黑色的,而且z是左孩子:变换z的父亲和爷爷的颜色,并对爷爷做右旋,这样便不再有两个红色的节点了,循环结束。
删除
删除前面的操作也和普通二叉查找树一样,然后调用一个辅助程序RBDeleteFixup(T,k),用来改变节点的颜色并作旋转以保持红黑树性质。时间也为O(lgn).只有当需要删除的节点是黑色时,才需要调用RBDeleteFixup(T,k),因为黑高度变了,破坏了性质5,此时补救的办法是把节点x(key[x] = k)视为还有额外的一重黑色,这样性质5就不会变了,去除额外黑色的方法是在while循环中不断将黑色上移(不断地改变颜色和旋转),直到:
1)x指向一个红色节点,此时将x单独着为黑色;
2)x指向根,此事简单地消除额外的黑色
在while循环中,节点x总是指向具有双重黑色的非根节点。
RBDeleteFixup需要处理四种情况:
1)x的兄弟w是红色的:改变父亲和w的颜色,并对父亲做左旋,此时转为情况2)、3)或4)。
2)x的兄弟w是黑色的,而且w的两个孩子都是黑色的:从x和w上去除一重黑色,从而x为黑色而w为红色,为了补偿,p[x]加上一重额外的黑色,令p[x]为新的x继续循环
3)x的兄弟w是黑色的,w的左孩子是红色的,右孩子是黑色的:改变w及其左孩子的颜色,并对w进行右旋,红黑性质依然保持,此时进入情况4
4)x的兄弟w是黑色的,而且w的右孩子是红色的:改变p[x]和w的颜色,并对p[x]做左旋,此时x的额外黑色可去除而变为单独黑色。此时直接令x=root[T],可结束循环。
注:这些情况不是相互排斥的,可从一种情况转为另一种情况,也可以直接进入某一情况
(以上皆参考《算法导论》)
代码如下:
#include <cstdio>
#include <cstdlib>
#include <queue>
using namespace std;
typedef struct treeNode *RBtree;
enum kindOfColor{RED, BLACK};
struct treeNode{
int key;
RBtree left;
RBtree right;
RBtree parent;
int color;
};
RBtree NIL = (RBtree)malloc(sizeof(struct treeNode));
RBtree search(RBtree T, int k){
if (T == NIL){
return NIL;
}
RBtree P = T;
while (P != NIL && P->key != k){
if (P->key > k){
P = P->left;
}
else{
P = P->right;
}
}
return P;
}
RBtree makeNode(int k){
RBtree z = (RBtree)malloc(sizeof(struct treeNode));
z->key = k;
z->left = z->right = z->parent = NIL;
z->color = RED;
return z;
}
RBtree findMin(RBtree T)
{
if (T == NIL)
return NIL;
RBtree p = T;
while (p->left != NIL)
{
p = p->left;
}
return p;
}
//x的后继即具有大于key[x]中的关键字最小的那个节点
//节点的后继主要有两种情况:1。z的右子树非空,则其后继为右子树中最小节点;2.右子树为空,则后继为为x的最小祖先节点
RBtree treeSuccessor(RBtree T, RBtree z){
if (T == NIL){
return NIL;
}
if (z != NIL){
if (z->right != NIL){
return findMin(z->right);
}
//寻找z的最小祖先
RBtree p = z->parent;
while (p != NIL && p->right == z){
z = p;
p = p->parent;
}
return p;
}
return NIL;
}
//左旋转则左下降
RBtree leftRotate(RBtree *T, RBtree x){
RBtree y = x->right;
x->right = y->left;
if (y->left != NIL){
y->left->parent = x;
}
if (x->parent == NIL){
*T = y;
(*T)->parent = NIL;
}
else{
y->parent = x->parent;
if (x == x->parent->left){
x->parent->left = y;
}
else{
x->parent->right = y;
}
}
x->parent = y;
y->left = x;
return y;
}
//右旋转则右下降
RBtree rightRotate(RBtree *T, RBtree x){
RBtree y = x->left;
x->left = y->right;
if (y->right != NIL){
y->right->parent = x;
}
if (x->parent == NIL){
*T = y;
(*T)->parent = NIL;
}
else{
y->parent = x->parent;
if (x == x->parent->left){
x->parent->left = y;
}
else{
x->parent->right = y;
}
}
x->parent = y;
y->right = x;
return y;
}
RBtree RBInsertFixup(RBtree T, RBtree z){
while (z->parent->color == RED){
if (z->parent == z->parent->parent->left){
RBtree y = z->parent->parent->right;
//情况一:z的叔叔是红色的,将z的父亲和叔叔变为黑色,爷爷变为红色
if (y->color == RED){
z->parent->color = BLACK;
y->color = BLACK;
y->parent->color = RED;
z = z->parent->parent;
}
else{
//情况二:z的叔叔y是黑色的,且z是右孩子。对在的父亲做左旋
if (z == z->parent->right){
z = leftRotate(&T, z->parent);
z = z->left;
}
//情况二转入情况三:z的叔叔y是黑色的,且z是左孩子。改变z的父亲和爷爷的颜色,并对爷爷做右旋
z->parent->color = BLACK;
z->parent->parent->color = RED;
z->parent = rightRotate(&T, z->parent->parent);
}
}
else{
RBtree y = z->parent->parent->left;
if (y->color == RED){//情况一:z的叔叔是红色的
z->parent->color = BLACK;
y->color = BLACK;
y->parent->color = RED;
z = z->parent->parent;
}
else{
if (z == z->parent->left){//情况二:z的叔叔y是黑色的,且z是左孩子
z = rightRotate(&T, z->parent);
z = z->right;
}
z->parent->color = BLACK;//情况二转入情况三:z的叔叔y是黑色的,且z是右孩子
z->parent->parent->color = RED;
z->parent = leftRotate(&T, z->parent->parent);
}
}
}
T->color = BLACK;
return T;
}
//先找到要插入的位置,再进行层层向上的修改RBInsertFixup
RBtree RBInsert(RBtree T, int k){
RBtree z = makeNode(k);
RBtree x = T;
RBtree y = NIL;
while (x != NIL){
y = x;
if (x->key > z->key){
x = x->left;
}
else{
x = x->right;
}
}
z->parent = y;
if (y == NIL){
T = z;
}
else{
if (y->key > z->key){
y->left = z;
}
else{
y->right = z;
}
}
z->left = z->right = NIL;
z->color = RED;
T = RBInsertFixup(T, z);
return T;
}
RBtree RBDeleteFixup(RBtree T, RBtree x){
RBtree w = NIL; //x的兄弟
while (x != T && x->color == BLACK){
if (x == x->parent->left){
w = x->parent->right;
if (w->color == RED){//情况一:x的兄弟w是红色的
w->color = BLACK;
x->parent->color = RED;
x->parent->parent = leftRotate(&T, x->parent);
w = x->parent->right;
}
//情况二:x的兄弟w是黑色的,而且w的两个孩子都是黑色的
if (w->left->color == BLACK && w->right->color == BLACK){
w->color = RED;
x = x->parent;
}
//情况三:x的兄弟w是黑色的,w的左孩子是红色的,右孩子是黑色的:转为情况四
if (w->left->color == RED && w->right->color == BLACK){
w->left->color = BLACK;
w->color = RED;
w = rightRotate(&T, w);
}
//情况四:w是黑色的,w的右孩子是红色的
if (w->right->color == RED){
w->color = x->parent->color;
x->parent->color = BLACK;
w->right->color = BLACK;
x->parent->parent = leftRotate(&T, x->parent);
x = T;
}
}
else{
w = x->parent->left;
if (w->color == RED){//情况一:x的兄弟w是红色的
w->color = BLACK;
x->parent->color = RED;
x->parent->parent = rightRotate(&T, x->parent);
w = x->parent->left;
}
//情况二:x的兄弟w是黑色的,而且w的两个孩子都是黑色的
if (w->left->color == BLACK && w->right->color == BLACK){
w->color = RED;
x = x->parent;
}
//情况三:x的兄弟w是黑色的,w的右孩子是红色的,左孩子是黑色的:转为情况四
if (w->right->color == RED && w->left->color == BLACK){
w->right->color = BLACK;
w->color = RED;
w = leftRotate(&T, w);
}
if (w->left->color == RED){
w->color = x->parent->color;
x->parent->color = BLACK;
w->left->color = BLACK;
x->parent->parent = rightRotate(&T, x->parent);
x = T;
}
}
}
x->color = BLACK;
return T;
}
//先和普通二叉树一样删除节点,再做修改RBDeleteFixup
RBtree RBDelete(RBtree T, int k){
RBtree z = search(T, k);
RBtree y = NIL, x = NIL;
if (z->left == NIL || z->right == NIL){
y = z;
}
else{
y = treeSuccessor(T, z);
}
if (y->left != NIL){
x = y->left;
}
else{
x = y->right;
}
x->parent = y->parent;
if (y->parent == NIL){
T = x;
}
if (y == y->parent->left){
y->parent->left = x;
}
else{
y->parent->right = x;
}
if (y != z){
z->key = y->key;
}
if (y->color == BLACK){
T = RBDeleteFixup(T, x);
}
return T;
}
void levelOrder(RBtree T){
queue<RBtree> Q;
Q.push(T);
RBtree p;
printf("层序遍历:");
while (!Q.empty()){
p = Q.front();
if (p->left != NIL){
Q.push(p->left);
}
if (p->right != NIL){
Q.push(p->right);
}
printf("%d ", p->key);
Q.pop();
}
printf("\n");
}
int main(void){
NIL->left = NIL->right = NIL->parent = NIL;
NIL->color = BLACK;
NIL->key = INT_MIN;
RBtree T = NIL;
T = RBInsert(T, 16);
levelOrder(T);
T = RBInsert(T, 11);
levelOrder(T);
T = RBInsert(T, 12);
levelOrder(T);
T = RBInsert(T, 1);
levelOrder(T);
T = RBInsert(T, 3);
levelOrder(T);
T = RBDelete(T, 12);
levelOrder(T);
T = RBDelete(T, 16);
levelOrder(T);
T = RBDelete(T, 11);
levelOrder(T);
T = RBDelete(T, 1);
levelOrder(T);
return 0;
}