您的位置:首页 > 编程语言 > Go语言

红黑树(Red Black Tree)

2017-02-13 16:40 483 查看

1. 概述

一棵二叉树如果满足下面的红黑性质,则为一棵红黑树:

1)每个节点或是红的,或是黑的

2)根节点是黑的

3)每个叶节点(NIL)是黑的。

4)如果一个节点是红的,则它的儿子都是黑的

5)对每个节点,从该节点到其子孙节点的所有路径包含相同数目的黑节点

一棵二叉树的形状如下图:



2. 红黑树的高度

1)先证以x为根的子树至少包含

(利用红黑树的性质5即可证明)

2)一棵含有n个节点的红黑树的黑高度至少是h / 2,再由1)我们可以得到 

,所以

 。

因此可以得出红黑树的操作可以在O(lgn)时间内完成。

3.红黑树的基本数据结构

1)红黑树的叶节点

typedef struct Node{
Color color;
struct Node *left;
struct Node *right;
struct Node *p;
ElemType key;
}Node;
2)红黑树

typedef struct Tree{
Node *nillNode;
Node * root;
}*RedBlackTree;

4. 红黑树的两个基本操作----左旋和右旋



1)左旋代码参考

void leftRotate(RedBlackTree t, Node *x) {
// Suppose the right node exists(right node not null)
Node * y = x->right;
//turn y's left subtree into x's right subtree
x->right = y->left;
if(y->left != t->nillNode) {
y->left->p = x;
}
y->p = x->p;
if(x->p == t->nillNode) {      // x is the root of the tree
t->root = y;
} else if (x == x->p->left) {
x->p->left = y;
} else {
x->p->right = y;
}

// put x on y's leftg
y->left = x;
// link x->p to y
x->p = y;
}
2)右旋代码参考
void rightRotate(RedBlackTree t, Node *x) {
Node *y = x->left;
x->left = y->right;
if(y->left != t->nillNode) {
y->right->p = x;
}
y->p = x->p;
if(x->p == t->nillNode) {
t->root = y;
} else if (x == x->p->left) {
x->p->left = y;
} else {
x->p->right = y;
}
y->right = x;
x->p = y;
}

5. 最小值

和搜索二叉树差不多即一直往左遍历

// find the min key with x as the root
Node * min(RedBlackTree t, Node * x) {
if (x != t->nillNode) {
Node *y = x->left;
while (y != t->nillNode) {
x = y;
y = x->left;
}
}
return x;
}

6. 最大值

// find the min key with x as the root
Node * min(RedBlackTree t, Node * x) {
if (x != t->nillNode) {
Node *y = x->left;
while (y != t->nillNode) {
x = y;
y = x->left;
}
}
return x;
}

7. 后继

Node *successor(RedBlackTree t, Node * x) {
if(x->right != t->nillNode) {
return min(t, x->right);
}
Node * y = x->p;
while (y != t->nillNode && x == y->left) {
x = y;
y = y->p;
}
return y;
}

8. 构建一棵红黑树

// create red black tree and initial
void createTree(RedBlackTree *t) {
// global nil node
Node * nillNode = NULL;
// create nillNode and initial
nillNode = (Node *)malloc(sizeof(Node));
if(nillNode == NULL) {
printf("nilNode allocation failed!\n");
exit(1);
}
nillNode->color = BLACK;
nillNode->left = NULL;
nillNode->right = NULL;
nillNode->p = NULL;

//create Red Black Tree and initial
(*t) = (RedBlackTree) malloc(sizeof(struct Tree));
if(*t == NULL) {
printf("red black allocation failed!\n");
exit(-1);
}
(*t)->root = nillNode;
(*t)->nillNode = nillNode;
}

9. 插入

1)首先我们像二叉树那样把节点插入到对应的节点,并设置其为红色节点

2)我们对其进行调整,以维持红黑性质

在整个插入过程和调整过程中,只有可能违反红黑性质的2)和4)即根节点是黑色的以及红色节点的儿子节点是黑色节点。(在插入步骤1)中,很容易得出性质2)和4都可能违反),根据如下的调整过程,我们也很容易得出只有性质2)和性质4)可以被违反。



上述过程转化过程:



转化成伪代码:

if (case 1) {

} else {
if (case 2) {

}
case 3
}
从case1 到case3我们发现并没有违反性质1)3)5),而违反性质1)说明根是红色的,只要将其变成黑色的就OK,所有的性质都可以得到满足,剩下的所有情况就是违反性质4,当遇到case1时z是不断上升的,只要遇到case2 或case3,整个调整过程即将完成。

代码为:

// used for fix up when insert
void insertFixUp(RedBlackTree t, Node *z) {
Node * y = t->nillNode;
while (z->p->color == RED) {
if(z->p == z->p->p->left) {
// find z's uncle node
y = z->p->p->right;
// case 1
if (y->color == RED) {
z->p->color = BLACK;
y->color = BLACK;
z = z->p->p;
z->color = RED;
} else {    // y->color == Black
// case 2
if(z == z->p->right) {
z = z->p;
leftRotate(t, z);
}
// case 3
z->p->color = BLACK;
z->p->p->color = RED;
rightRotate(t, z->p->p);
}
} else {    // the same as above with "right"  and "left" exchanged
y = z->p->p->left;
//case 1
if (y->color == RED) {
z->p->color = BLACK;
y->color = BLACK;
z = z->p->p;
z->color = RED;
} else {
// case 2
if(z == z->p->left) {
z = z->p;
rightRotate(t, z);
}
// case3:
z->p->color = BLACK;
z->p->p->color = RED;
leftRotate(t, z->p->p);
}
}
}
t->root->color = BLACK;
}

10. 删除

1)按照二叉搜索树进行删除

2)如果删除的是黑色节点,则为了维持红黑性质进行相应的调整

其过程代码如下:

void delete(RedBlackTree t, ElemType key) {
Node * z = find(t, key);
if (z != t->nillNode) {
// the node which will be deleted
Node *y = t->nillNode;
if (z->left == t->nillNode || z->right == t->nillNode) {
y = z;
} else {
y = successor(t, z);
}
Node *x;
if (y->left != t->nillNode) {
x = y->left;
} else {
x = y->right;
}
x->p = y->p;
if (y->p == t->nillNode) {
t->root = x;
} else {
if (y == y->p->left) {
y->p->left = x;
} else {
y->p->right = x;
}
}
if (y != z) {
z->key = y->key;
}
free(y);
if (y->color == BLACK) {
deleteFixUp(t, x);
}
}
}


我们知道如果删除的是红色的节点的话,则红黑性质依旧是可以满足的,若是删除的是黑色则情况稍有复杂。

可能违反如下性质:

违反性质2):如果删除的节点y原先是根节点,而y的一个红色的儿子节点成了根节点,则违反了性质2)

违反性质4):如果删除节点y的儿子节点x 和 其父亲节点p[y]都是红色的,则违反了性质4)

违反性质5):因为删除的节点是黑色的,必然使得经过该节点的路径黑高度少1,则违反性质5)

综上违反的性质,我们删除节点y的儿子节点x当做一个双重颜色节点,即附加一层黑色(若x原先是黑色的,现在有双重黑色属性,若是原先是黑色的则现在具有红和黑的性质),因此在调整过程中,认为性质5)是维持的。由于我们在调整过程中x节点左右子树是满足红黑树的所有性质的,所以对于违反性质2),我们只要使得让其根节点变成黑色的,则整个红黑树的性质是可以满足的。若对于违反性质4)我们只要使得红色节点x变成黑色的,则整棵树又满足了红黑树的所有性质。剩下内容就是使得双重颜色节点x变成真正意义上的双重颜色节点,即使得性质5)得以维持,调整过程如下。



case1 x的兄弟w是红色的

case2 x的兄弟w是黑色的,且两个孩子是黑色的

case3 x的兄弟w是黑色的,且w的左孩子是黑色的,右孩子是黑色的

case4 x的兄弟w是黑色的,且w的右孩子是红色的

从上述调整我们可以得出其转化图



尤其情况转化,我们可以得出其伪代码:

if (case 1) {

}
if (case 2) {

} else {
if (case 3) {

}
case 4
}
其中可能有人认为会在case1和case2中无限循环,而完成不了调整的目的。

因为case1,使得节点x下沉,而case2使得x节点上升,因此不得不会这样想。但我们多想一下,满足上述情况case1只能转化到case2,而case2只能转化到case1,当我们再往下走时,发现x继续上升,因为此时的x的兄弟节点w是黑色的而不是红色的,所以x会继续上升。因此可以想象整个x是不断朝根节点上升的。

调整代码:

void deleteFixUp(RedBlackTree t, Node *x) {
while (x != t->root && x->color == BLACK) {
// x is x's parent's left child
if (x == x->p->left) {
Node * w = x->p->right;
// case1:turn it into case2 or case3 or case4
if(w->color == RED) {
w->color = BLACK;
x->p->color = RED;
leftRotate(t,x->p);
w = x->p->right;
}
//case 2
if(w->left->color == BLACK && w->right->color == BLACK) {
w->color = RED;
x = x->p;
} else {
// case 3
if(w->right->color == BLACK) {
w->color = RED;
w->left->color = BLACK;
rightRotate(t, w);
w = x->p->right;
}
// case 4
w->color = x->p->color;
x->p->color = BLACK;
w->right->color = BLACK;
leftRotate(t,x->p);
x = t->root;
}
} else {
Node *w = x->p->left;
if (w->color == RED) {
w->color = BLACK;
x->p->color = RED;
rightRotate(t, x->p);
w = x->p->left;
}
if (w->left->color == BLACK && w->right->color == BLACK) {
w->color = RED;
x = x->p;
} else {
if(w->left->color == BLACK) {
w->color = RED;
w->right->color = BLACK;
leftRotate(t, w);
w = x->p->left;
}
w->color = x->p->color;
x->p->color = BLACK;
w->left->color = BLACK;
rightRotate(t, x->p);
x = t->root;
}
}
}
x->color = BLACK;
}


至此,所有的分析到此为止,有错误的和疑惑的请多多留言。

完整代码请转此



参考

本文主要是依据 Thomas H.Cormen、Charles E.Leiserson《算法导论》第二版 第十三章 红黑树

转载请注明

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息