很多人认为红黑树很难懂,其实红黑树并没有我们想象中的那么难
首先我们先看红黑树到底是干什么的
红黑树(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 -1void 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 redif(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 nodeif(z == z->p->right){z = z->p;left_rotate(T, z);}// Case3: z uncle node y is black, but z is left nodez->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;elsereturn 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;}elsereturn ERR;while(x != T->nil) //y is parent point of x{y = x;if(z->data < x->data)x = x->left;elsex = x->right;}z->p = y;if(y == T->nil)T->root = z;else if(z->data < y->data)y->left = z;elsey->right = z;z->left = T->nil;z->right = T->nil;z->color = 'R';if( rbtree_fixup(T, z) )return OK;elsereturn 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;elsereturn 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");}elseprintf("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树的联系,思路清晰。
共同学习,写下你的评论
评论加载中...
作者其他优质文章