很多人认为红黑树很难懂,其实红黑树并没有我们想象中的那么难
首先我们先看红黑树到底是干什么的
红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。
红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。
我们看到,关于二叉树基本上都是干这样的事,每次排除一个子树,递归另一个子树,使其时间复杂度为O(log n),然后以一种特殊的方法去维护他
比如说
50
25 60
13 30 55 88
这样的树,他的左孩子总是比父节点小,右孩子总是比父节点大,假设我们搜索55,其实就相当于我们先询问
节点1(50) 发现55比他大,然后去其右孩子问
节点3(60) 发现55比他小,然后去其左孩子问
节点6(55) 找到了~
就是这样。但如果我现在要插入一些数怎么办呢?比如说我要加一个31,很显然我应该放在30的右孩子
那么就变成了
50
25 60
13 30 55 88
31
我要再来一个32呢,还得往下,再来一个33呢? 所以我们看出,如果我不改变整个树的结构,那么我很难构造出真正实现O(log n)的二叉树
因为随着插入、删除等操作,其父节点不会再是一组数列的中心点了。那么到底要怎么改变树的结构,成为了不同查找树算法的区分。
好了,现在我们大概明白它要干什么事了,然后我们再看看红黑树为什么要分红和黑
这里我们要引用一下2-3树的知识。
2-3树是最简单的B-树(或-树)结构,其每个非叶节点都有两个或三个子女,而且所有叶都在统一层上。2-3树不是二叉树,其节点可拥有3个孩子。不过,2-3树与满二叉树相似。高为h的2-3树包含的节点数大于等于高度为h的满二叉树的节点数,即至少有2^h-1个节点。
就是说,为了保证查找树的平衡性,我们需要一些灵活性,因此在这里我们允许树中的一个结点保存多个键。
2-结点:含有一个键(及值)和两条链接,左链接指向的2-3树中的键都小于该结点,右链接指向的2-3树中的键都大于该结点。
3-结点:含有两个键(及值)和三条链接,左链接指向的2-3树中的键都小于该结点,中链接指向的2-3树中的键都位于该结点的两个键之间,右链接指向的2-3树中的键都大于该结点。
看了上篇博客线段树的同学可能会明白一些 http://blog.csdn.net/sm9sun/article/details/53302873
说白了,就是2-3树,他的值可以是某个点,也可以是一个线段。也就是说:
2叉:
【a】
【小于a】【大于a】
3叉:
【a,b】
【小于a】【ab之间】【大于b】
好了,那么红黑树和2-3树有什么联系呢??
红黑树的本质:
红黑树是对2-3查找树的改进,它能用一种统一的方式完成所有变换。
替换3-结点
红黑树背后的思想是用标准的二叉查找树(完全由2-结点构成)和一些额外的信息(替换3-结点)来表示2-3树。
我们将树中的链接分为两种类型:红链接将两个2-结点连接起来构成一个3-结点,黑链接则是2-3树中的普通链接。确切地说,我们将3-结点表示为由一条左斜的红色链接相连的两个2-结点。这种表示法的一个优点是,我们无需修改就可以直接使用标准二叉查找树的get()方法。对于任意的2-3树,只要对结点进行转换,我们都可以立即派生出一颗对应的二叉查找树。我们将用这种方式表示2-3树的二叉查找树称为红黑树。
*以下内容转载于http://blog.csdn.net/yang_yulei/article/details/26066409
所以我们看一下红黑树的性质到底在说些什么:
性质1. 节点是红色或黑色。(2-3树变型)
性质2. 根节点是黑色。
性质3 每个叶节点(NIL节点,空节点)是黑色的。
性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)(一个节点不可能与两条红链接相连)
性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。(黑链接(2叉)平衡)
接下来我们说一下为什么左旋、右旋,其实特别简单,就如上文说的一样,
父节点无法保证自己处于平衡状态,那么就必须通过旋转来保证树的平衡性
比如:
50
25 60
13 30
当我插入一个10时,左边的深度比右边深2层,那么就必须需要旋转
那么就变成了
25
13 50
10 30 60
所以具体的着色、旋转操作要看我们插入删除不同的情况而定
*以下内容转载于http://blog.csdn.net/v_JULY_v/article/details/6105630
红黑树插入的几种情况:
情况1,z的叔叔y是红色的。
情况2:z的叔叔y是黑色的,且z是右孩子
情况3:z的叔叔y是黑色的,且z是左孩子
红黑树删除的几种情况。
情况1:x的兄弟w是红色的。
情况2:x的兄弟w是黑色的,且w的俩个孩子都是黑色的。
情况3:x的兄弟w是黑色的,且w的左孩子是红色,w的右孩子是黑色。
情况4:x的兄弟w是黑色的,且w的右孩子是红色的。
设:要插入的节点为N,父亲节点P,祖父节点G,叔叔节点U,兄弟节点S
如下图所示,找一个节点的祖父和叔叔节点:
node grandparent(node n) //祖父
{
return n->parent->parent;
}
node uncle(node n) //叔叔
{
if (n->parent == grandparent(n)->left)
return grandparent(n)->right;
else
return grandparent(n)->left;
}
红黑树插入的几种情况
情形1: 新节点N位于树的根上,没有父节点
void insert_case1(node n) {
if (n->parent == NULL)
n->color = BLACK;
else
insert_case2(n);
}
情形2: 新节点的父节点P是黑色
void insert_case2(node n) {
if (n->parent->color == BLACK)
return; /* 树仍旧有效 */
else
insert_case3(n);
}
情形3:父节点P、叔叔节点U,都为红色,
void insert_case3(node n) {
if (uncle(n) != NULL && uncle(n)->color == RED) {
n->parent->color = BLACK;
uncle(n)->color = BLACK;
grandparent(n)->color = RED;
insert_case1(grandparent(n)); //因为祖父节点可能是红色的,违反性质4,递归情形1.
}
else
insert_case4(n); //否则,叔叔是黑色的,转到下述情形4处理。
情形4: 父节点P是红色,叔叔节点U是黑色或NIL;
插入节点N是其父节点P的右孩子,而父节点P又是其父节点的左孩子。
void insert_case4(node n) {
if (n == n->parent->right && n->parent == grandparent(n)->left) {
rotate_left(n->parent);
n = n->left;
} else if (n == n->parent->left && n->parent == grandparent(n)->right) {
rotate_right(n->parent);
n = n->right;
}
insert_case5(n); //转到下述情形5处理。
情形5: 父节点P是红色,而叔父节点U 是黑色或NIL,
要插入的节点N 是其父节点的左孩子,而父节点P又是其父G的左孩子。
void insert_case5(node n) {
n->parent->color = BLACK;
grandparent(n)->color = RED;
if (n == n->parent->left && n->parent == grandparent(n)->left) {
rotate_right(grandparent(n));
} else {
/* 反情况,N 是其父节点的右孩子,而父节点P又是其父G的右孩子 */
rotate_left(grandparent(n));
}
}
红黑树删除的几种情况
上文我们约定,兄弟节点设为S,我们使用下述函数找到兄弟节点:
struct node * sibling(struct node *n) //找兄弟节点
{
if (n == n->parent->left)
return n->parent->right;
else
return n->parent->left;
}
情况1: N 是新的根。
void
delete_case1(struct node *n)
{
if (n->parent != NULL)
delete_case2(n);
}
情形2:兄弟节点S是红色
void delete_case2(struct node *n)
{
struct node *s = sibling(n);
if (s->color == RED) {
n->parent->color = RED;
s->color = BLACK;
if (n == n->parent->left)
rotate_left(n->parent); //左旋
else
rotate_right(n->parent);
}
delete_case3(n);
}
情况 3: 兄弟节点S是黑色的,且S的俩个儿子都是黑色的。但N的父节点P,是黑色。
void delete_case3(struct node *n)
{
struct node *s = sibling(n);
if ((n->parent->color == BLACK) &&
(s->color == BLACK) &&
(s->left->color == BLACK) &&
(s->right->color == BLACK)) {
s->color = RED;
delete_case1(n->parent);
} else
delete_case4(n);
}
情况4: 兄弟节点S 是黑色的、S 的儿子也都是黑色的,但是 N 的父亲P,是红色。
void delete_case4(struct node *n)
{
struct node *s = sibling(n);
if ((n->parent->color == RED) &&
(s->color == BLACK) &&
(s->left->color == BLACK) &&
(s->right->color == BLACK)) {
s->color = RED;
n->parent->color = BLACK;
} else
delete_case5(n);
}
情况5: 兄弟S为黑色,S 的左儿子是红色,S 的右儿子是黑色,而N是它父亲的左儿子。
//此种情况,最后转化到下面的情况6。
void delete_case5(struct node *n)
{
struct node *s = sibling(n);
if (s->color == BLACK)
if ((n == n->parent->left) &&
(s->right->color == BLACK) &&
(s->left->color == RED)) {
// this last test is trivial too due to cases 2-4.
s->color = RED;
s->left->color = BLACK;
rotate_right(s);
} else if ((n == n->parent->right) &&
(s->left->color == BLACK) &&
(s->right->color == RED)) {
// this last test is trivial too due to cases 2-4.
s->color = RED;
s->right->color = BLACK;
rotate_left(s);
}
}
delete_case6(n); //转到情况6
情况6: 兄弟节点S是黑色,S的右儿子是红色,而 N 是它父亲的左儿子。
[对应我第二篇文章中,情况4:x的兄弟w是黑色的,且w的右孩子时红色的。]
void delete_case6(struct node *n)
{
struct node *s = sibling(n);
s->color = n->parent->color;
n->parent->color = BLACK;
if (n == n->parent->left) {
s->right->color = BLACK;
rotate_left(n->parent);
} else {
s->left->color = BLACK;
rotate_right(n->parent);
}
}
红黑树完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef int DataTypedef;
typedef struct RBTREENODE
{
int data;
char color;
struct RBTREENODE *left;
struct RBTREENODE *right;
struct RBTREENODE *p;
}rbtreeNode;
typedef struct RBTREE
{
rbtreeNode *root;
rbtreeNode *nil;
}rbTree;
#define OK 1
#define ERR -1
void rbtree_free_node(rbTree *T, rbtreeNode *p)
{
if(p != T->nil)
{
rbtree_free_node(T, p->left);
rbtree_free_node(T, p->right);
free(p);
}
}
void rbtree_show_node(rbTree *T, rbtreeNode *p)
{
if(p != T->nil)
{
printf("%02d ", p->data);
p->color == 'R' ? printf(" R\n") : printf(" B\n");
rbtree_show_node(T, p->left);
rbtree_show_node(T, p->right);
}
}
void left_rotate(rbTree *T, rbtreeNode * x)
{
rbtreeNode *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;
}
void right_rotate(rbTree *T, rbtreeNode *x)
{
rbtreeNode * y = x->left;
x->left = y->right;
if(y->right != T->nil)
y->right->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->right = x;
x->p = y;
}
int rbtree_fixup(rbTree *T, rbtreeNode *z)
{
rbtreeNode *y = NULL;
/*
* while循环在每次迭代的开头都保持3个部分的不变式
* 1 节点z是红色节点
* 2 如果z.p是根节点,则z.p是黑节点
* 3 如果有任何红黑性质被破坏,则至多只有一条被破坏,或是性质2(根为黑色节点),或是性质4(如果一个节点是红色的,
* 则它的两个子节点都是黑色的)
*/
while(z->p->color == 'R')
{
if(z->p == z->p->p->left)//在所有的情况中,z的祖父节点z.p.p是黑色的,以为z.p是红色的
{
y = z->p->p->right;
// Case1: z uncle node y is red
if(y->color == 'R')
{
z->p->color = 'B';
y->color = 'B';
z->p->p->color = 'R';
z = z->p->p;
}
else
{
// Case2: z uncle node y is black, but z is right node
if(z == z->p->right)
{
z = z->p;
left_rotate(T, z);
}
// Case3: z uncle node y is black, but z is left node
z->p->color = 'B';
z->p->p->color = 'R';
right_rotate(T, z->p->p);
}
}
else
{
y = z->p->p->left;
if(y->color == 'R')
{
z->p->color = 'R';
y->color = 'B';
z->p->p->color = 'R';
z = z->p->p;
}
else
{
if(z == z->p->left)
{
z = z->p;
right_rotate(T, z);
}
z->p->color = 'B';
z->p->p->color = 'R';
left_rotate(T, z->p->p);
}
}
}
T->root->color = 'B';
return OK;
}
/*
* blow funtion is used in other file
*/
rbTree * rbtree_init()
{
rbTree *T = NULL;
T = (rbTree *)malloc(sizeof(rbTree));
if(T == NULL)
{
printf("T no space\n");
}
T->nil = (rbtreeNode *)malloc(sizeof(rbtreeNode));
if(T->nil == NULL)
{
printf("T->nil no space\n");
}
T->root = T->nil;
T->nil->data = (1<<31);
T->nil->color = 'B';
T->nil->left = T->nil;
T->nil->right = T->nil;
T->nil->p = T->nil;
return T;
}
int rbtree_in(rbTree *T, DataTypedef x)
{
rbtreeNode *p = T->root;
while(p != T->nil)
{
if(x < p->data)
p = p->left;
else if(x > p->data)
p = p->right;
else
return OK;
}
return ERR;
}
int rbtree_insert(rbTree *T, DataTypedef num)
{
rbtreeNode *y = T->nil;
rbtreeNode *x = T->root;
rbtreeNode *z = NULL;
if(!rbtree_in(T, num))
{
z = (rbtreeNode *)malloc(sizeof(rbtreeNode));
if(z == NULL)
{
printf("no space for z in rbtree\n");
}
z->data = num;
}
else
return ERR;
while(x != T->nil) //y is parent point of x
{
y = x;
if(z->data < x->data)
x = x->left;
else
x = x->right;
}
z->p = y;
if(y == T->nil)
T->root = z;
else if(z->data < y->data)
y->left = z;
else
y->right = z;
z->left = T->nil;
z->right = T->nil;
z->color = 'R';
if( rbtree_fixup(T, z) )
return OK;
else
return ERR;
}
rbtreeNode *rbtree_find(rbTree *T, DataTypedef x)
{
rbtreeNode *p = T->root;
while(p != T->nil)
{
if(x < p->data)
p = p->left;
else if(x > p->data)
p = p->right;
else
return p;
}
return T->nil;
}
rbtreeNode *rbtree_findmin(rbTree *T, rbtreeNode *p)
{
rbtreeNode *q = p;
while(p != T->nil)
{
q = p;
p = p->left;
}
return q;
}
void rbtree_transplant(rbTree *T, rbtreeNode *u, rbtreeNode *v)
{
if(u->p == T->nil)
{
T->root = v;
}
else if(u == u->p->left)
{
u->p->left = v;
}
else
{
u->p->right = v;
}
v->p = u->p;
}
void rbtree_delete_fixup(rbTree *T, rbtreeNode *x)
{
rbtreeNode *w;
while(x != T->root && x->color == 'B')//while循环中,x总是指向一个具有双重黑色的非根界节点
{
if(x == x->p->left)
{
w = x->p->right;
if(w->color == 'R') //情况1:x的兄弟节点w是红色
{
w->color = 'B';
x->p->color = 'R';
left_rotate(T, x->p);
w = x->p->right;
}
if(w->left->color == 'B' && w->right->color == 'B') //情况2:x的兄弟节点w是黑色,而且w的两个子节点都是黑色的
{
w->color = 'R';
x = x->p;
}
else
{
if(w->right->color == 'B') //情况3:x的兄弟节点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的
{
w->left->color = 'B';
w->color = 'R';
right_rotate(T, w);
w = x->p->right;
}
w->color = x->p->color; //情况4:x的兄弟节点w是黑色的,且w的右孩子是红色的
x->p->color = 'B';
w->right->color = 'B';
left_rotate(T, x->p);
x = T->root;
}
}
else//x = x->p->right
{
w = x->p->left;
if(w->color == 'R')
{
w->color = 'B';
x->p->color = 'R';
right_rotate(T, x->p);
w = x->p->left;
}
if(w->left->color == 'B' && w->right->color == 'B')
{
w->color = 'R';
x = x->p;
}
else
{
if(w->left->color == 'B')
{
w->right->color = 'B';
w->color = 'R';
left_rotate(T, w);
w = x->p->left;
}
w->color = x->p->color;
x->p->color = 'B';
w->left->color = 'B';
right_rotate(T, x->p);
x = T->root;
}
}
}
x->color = 'B';
}
void rbtree_delete(rbTree *T, DataTypedef data)
{
rbtreeNode *z = rbtree_find(T, data);
rbtreeNode *y;
rbtreeNode *x;
char y_oldcolor;
if(z == T->nil)
{
printf("the rbtree hasn't %d\n", data);
return;
}
y = z;
y_oldcolor = y->color;
if(z->left == T->nil)
{
x = z->right;
rbtree_transplant(T, z, z->right);
}
else if(z->right == T->nil)
{
x = z->left;
rbtree_transplant(T, z, z->left);
}
else
{
y = rbtree_findmin(T, z->right);
y_oldcolor = y->color;
x = y->right;
if(y->p == z)
{
x->p = y;
}
else
{
rbtree_transplant(T, y, y->right);
y->right = z->right;
y->right->p = y;
}
rbtree_transplant(T, z, y);
y->left = z->left;
y->left->p = y;
y->color = z->color;
}
if(y_oldcolor == 'B')
rbtree_delete_fixup(T, x);
free(z);
//printf("free the node is ok\n\n");
}
void rbtree_show(rbTree *T)
{
if(T->root != T->nil)
{
rbtree_show_node(T, T->root);
printf("\n");
}
else
printf("This rbtree is empty.\n");
}
int rbtree_free(rbTree *T)
{
if(T->root != T->nil)
{
rbtree_free_node(T, T->root);
}
free(T->nil);
free(T);
return OK;
}
int main(void)
{
rbTree *T = NULL;
T = rbtree_init();
int n,value;
while(~scanf("%d",&n)&&n)
{
switch(n)
{
case 1:scanf("%d",&value);rbtree_insert(T, value);break;
case 2:rbtree_show(T);break;
case 3:scanf("%d",&value); rbtree_delete(T, value);break;
default:break;
}
}
rbtree_free(T);
return 0;
}
最后,推荐本人认为两个比较不错的博客,强烈建议大家去看一下
http://blog.csdn.net/v_JULY_v/article/details/6105630 含多篇关于红黑树的讲解,内容详细。
http://blog.csdn.net/yang_yulei/article/details/26066409 直接说明红黑树与2-3树的联系,思路清晰。