红黑树
上一篇中讲到,一棵高度为h的二叉搜索树,它所进行的操作都可以在O(h)时间内完成。因此搜索树的高度较低时,可以较快的完成。但是,如果树的高度较高时,这些集合操作可能并不比链表上快。如何实现一种搜索树的结构,使得其任何一种基本操作都可以在O(lgn)的时间内完成呢?
红黑树,就是这种“平衡”搜索树中的一种。可以保证最坏的情况下,基于动态集合操作的时间复杂度为O(lgn)。
红黑树,也是一种二叉搜索树,也支持search, predecessor, successor, minimum, maximum, insert 和 delete等操作,但是却有着和普通二叉搜索树不同的性质,于是也导致它的某些操作的复杂性,但是以code的复杂度换取时间的复杂度,不亦乐乎。
下面开始一起走入红黑树吧:
1、性质:
(1)每个结点要么是红色,要么是黑色;
(2)根结点是黑色的;
(3)每个叶子结点是黑色的。
(4)如果一个结点是红色的,则它的两个子结点都是黑色的。
(5)对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。
2、准备知识 --->旋转
搜索树在insert 和 delete的时候可能违反它的一些性质,因此必须改变某些结点的颜色和指针,以维持它的性质不变。指针的修改便是通过“旋转"来完成的。
(1)左转
Left_Rotate( T, x)
y = x.right
x.right = y.left
if y.left != T.NIL
y.left.p = x
y.p = x.p
if x.p = T.NIL
T.root = y
else if x = x.p.left
x.p.left = y
else
x.p.right = y
y.left = x
x.p = y
右转可以对称来实现。
3、插入
我们可以依据普通二叉搜索树的insert过程插入一个新的结点z, 并将其着为红色(为了尽量避免对红黑树进行调整)。看看插入新的结点z后对它的性质有什么影响:
(1)如果插入的是根结点,则违背了性质2,简单地将其着为黑色即可。
(2)如果插入结点后,它的父结点是红色,则违背了性质4,针对这种情况的调整方案分析如下:
4、删除
删除一个结点,延用普通二叉搜索树的删除方案:
若要删除的结点,它有非双空子结点,则可以直接删除该结点,让它的唯一子结点指向它。若子结点都为空,则用NULL取代原来的结点。若子结点均非空,则用它的后继结点的值取代它的值,然后以同样的方式删除它的后继结点即可。(所以,要删除的结点是z,那么实际删除的是它的后继结点y,而这么一个删除的过程是一个递归过程,慢慢体会吧)
但是在删除节点后,原来红黑树的性质可能被改变,如果被删除的结点是红色的结点,那么一切OK。如果被删除 结点是黑色的话,可能就会破坏它的某些性质:
(1)如果要删除的结点不是唯一的树结点,那么从它为根结点,到各个支路的黑高可能会受到影响,从而破坏性质5
(2)如果被删除的结点是根结点,而它的唯一子结点是红色,则破坏了性质1.
如下,是我在纸张画的分析过程,直接贴上来:
至此已分析完毕。贴上实现的代码:
// RB_Tree_.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
using namespace std;
enum Color
{
Black,
Red
};
typedef struct RB_Node
{
Color color;
int key;
struct RB_Node *left;
struct RB_Node *right;
struct RB_Node *p;
}RB_Node, *p_RB_Node;
RB_Node *T_NULL = NULL;
RB_Node * FindNode(RB_Node *root, int k)
{
while(root!=NULL && k!=root->key)
{
if(k<root->key)
root = root->left;
else
root = root->right;
}
if(root == NULL)
return NULL;
else
return root;
}
RB_Node * RB_Tree_Minimum(RB_Node *x)
{
RB_Node *y = T_NULL;
while(x!=T_NULL)
{
y = x;
x = x->left;
}
return y;
}
RB_Node * Succssor(RB_Node *root, RB_Node *x)
{
if(x->right !=T_NULL)
return RB_Tree_Minimum(x->right);
RB_Node *y = x->p;
while(y!=T_NULL && x == y->right)
{
x = y;
y = y->p;
}
return y;
}
void Left_Rotate(p_RB_Node *root, RB_Node *x)
{
RB_Node *y = x->right;
x->right = y->left;
if(y->left!=T_NULL)
y->left->p = x;
y->p = x->p;
if(x->p == T_NULL)
*root = y;
if(x == x->p->left)
x->p->left = y;
else
x->p->right = y;
y->left = x;
x->p = y;
}
void Right_Rotate(p_RB_Node *root, RB_Node *x)
{
RB_Node *y = x->left;
x->left = y->right;
if(y->right != T_NULL)
y->right->p = x;
y->p = x->p;
if(x->p ==T_NULL)
*root = y;
if(x == x->p->left)
x->p->left = y;
else
x->p->right = y;
y->right = x;
x->p = y;
}
void RB_Insert_FixUP(p_RB_Node *root, RB_Node *z)
{
/*
1.z是要插入的节点,p是其父节点,G是祖父节点,W是叔父节点;存在两种情况:p = G.left and p = G.right
注意破坏的是什么性质,去fix的是什么性质。
*/
while(z->p->color == Red)
{
RB_Node *p = z->p;
RB_Node *G = z->p->p;
RB_Node *W = T_NULL;
if(p == G->left)
{
W = G->right;
//case 1:z的父节点p和叔父节点W均是红色,那么G一定是黑色,则将p和w均着为黑色,将G着为红色
if( W->color ==Red)
{
p->color = Black;
W->color = Black;
G->color = Red;
z = G;
}
//case 2:w是黑色,且z是其父节点的右孩子,那么将p作为当前节点,并以它为支点,左旋
else if(z == p->right)
{
z = p;
Left_Rotate(root,p);
}
//case 3:w是黑色,z是p的左孩子,将p,G的颜色交换,然后以G为支点,右旋。
else
{
p->color = Black;
G->color = Red;
Right_Rotate(root,G);
}
}
else
{
W = G->left;
if( W->color ==Red)
{
p->color = Black;
W->color = Black;
G->color = Red;
z = G;
}
else if(z == p->left)
{
z = p;
Right_Rotate(root,p);
}
else
{
p->color = Black;
G->color = Black;
Left_Rotate(root,G);
}
}
}
(*root)->color = Black;
}
void RB_Insert(p_RB_Node *root, int k)
{
RB_Node *x = *root;
RB_Node *z = new RB_Node;
RB_Node *temp = T_NULL;
z->color = Red;
z->key = k;
z->left = z->right = z->p = T_NULL;
while(x!=T_NULL)
{
temp = x;
if(k<x->key)
x = x->left;
else
x = x->right;
}
z->p = temp;
if(temp == T_NULL)
*root = z;
else if(k<temp->key)
z->p->left = z;
else
z->p->right = z;
if(z->p==T_NULL)
z->color = Black;
else
RB_Insert_FixUP(root,z);
//delete z;
}
void RB_Delete_Fixup(p_RB_Node *root, RB_Node *x)
{
while(x!=(*root)&& x->color != Red)
{
RB_Node *p = x->p;
RB_Node *w = T_NULL; //w为x的兄弟节点
if(x == p->left)
{
w = p->right;
//case 1: 兄弟w 为红色,那么p一定是黑色,p与w换颜色,然后以p 为支点左旋
if(w->color == Red)
{
w->color = Black;
p->color = Red;
Left_Rotate(root,p);
}
//case 2: 兄弟为黑色,且兄弟的两孩子也均为黑色,分别提取x和w的一层黑,加到p上,以p作为新的x 结点,继续递归
if(w->left->color == Black && w->right->color == Black)
{
w->color = Red;
p->color = Black;
x = p;
}
//case 3:兄弟为黑色,左孩子为红色,右孩子为黑色。
//w与w.left交换颜色,然后以w为支点右旋---->到达case 4
else if(w->left->color == Red && w->right->color == Black)
{
w->left->color = Black;
w->color = Red;
Right_Rotate(root,w);
}
//case 4:兄弟为黑色,右孩子为红色,左孩子任意。
//w着为p的颜色,p着为黑色,兄弟右子结点染黑。然后以p为支点左旋,最终恢复红黑树的性质
else if(w->right->color == Red)
{
w->color = p->color;
p->color = Black;
w->right->color = Black;
Left_Rotate(root,p);
x =(*root);
}
}
else
{
w = p->left;
if(w->color == Red)
{
w->color = Black;
p->color = Red;
Right_Rotate(root,p);
}
if(w->left->color == Black && w->right->color == Black)
{
w->color = Red;
p->color = Black;
x = p;
}
else if(w->right->color == Red && w->left->color == Black)
{
w->right->color = Black;
w->color = Red;
Left_Rotate(root,w);
}
else if(w->left->color == Red)
{
w->color = p->color;
p->color = Black;
w->left->color = Black;
Right_Rotate(root,p);
x = (*root);
}
}
}
(*root)->color = Black;
}
void RB_Delete_Node(p_RB_Node *root, int k)
{
//z是要删除的节点,y是实际上被删除的节点,x是y节点的孩子
RB_Node *z = FindNode(*root,k);
RB_Node *y = T_NULL;
RB_Node *x = T_NULL;
if(z->left == T_NULL && z->right == T_NULL)
y = z;
else
y = Succssor(*root,z);
if(y->left !=T_NULL)
x = y->left;
else
x = y->right;
x->p = y->p;
if(x->p == T_NULL)
*root = x;
else if(y == y->p->left)
y->p->left = x;
else
y->p->right = x;
if(y!=z)
z->key = y->key;
if(y->color == Black)
RB_Delete_Fixup(root,x);
}
void RB_Tree_Inoder(RB_Node *root)
{
if(root!=T_NULL)
{
RB_Tree_Inoder(root->left);
printf("%d ,",root->key);
RB_Tree_Inoder(root->right);
}
}
void RB_Tree_Create(p_RB_Node *root)
{
int a[] = {12,1,9,2,0,11,7,19,4,15,18,5,14,13,10,16,6,3,8,17};
for(int i = 0;i<20;i++)
{
RB_Insert(root,a[i]);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
T_NULL = new RB_Node;
T_NULL->color = Black;
T_NULL->left = NULL;
T_NULL->right = NULL;
T_NULL->p = NULL;
T_NULL->key = 0;
RB_Node *root = T_NULL;
RB_Tree_Create(&root);
RB_Tree_Inoder(root);
cout<<endl;
RB_Delete_Node(&root,12);
RB_Tree_Inoder(root);
cout<<endl;
RB_Delete_Node(&root,1);
RB_Tree_Inoder(root);
cout<<endl;
RB_Delete_Node(&root,9);
RB_Tree_Inoder(root);
cout<<endl;
return 0;
}