我将从下面几个 方分析红黑树
1 红黑树的应用场景
2 红黑树的数学证明与推导
3 手撕红黑树的左旋与右旋
4 红黑树添加的实现与添加三种情况
5 红黑树删除的实现与删除四种情况
6 红黑树的线程安全的做法
7 分析红黑树工程实用的特点
1 红黑树的应用场景
红黑树主要有两种应用方向 1 红黑树的中序遍历是有序的。 2 红黑树存储的数据是key-value
1 Linux进程调度CFS 利用红黑树的中序遍历有序
2 Nignx Timer事件管理 利用红黑树的中序遍历有序
3 epoll时间块的管理
2 红黑树的数学证明与推导
下面通过"数学归纳法"开始论证高度为h的红黑树,它的包含的内节点个数至少为 2bh(x)-1个"。
(01) 当树的高度h=0时,
内节点个数是0,bh(x) 为0,2bh(x)-1 也为 0。显然,原命题成立。
(02) 当h>0,且树的高度为 h-1 时,它包含的节点个数至少为 2bh(x)-1-1。这个是根据(01)推断出来的!
下面,由树的高度为 h-1 的已知条件推出“树的高度为 h 时,它所包含的节点树为 2bh(x)-1”。
当树的高度为 h 时,
对于节点x(x为根节点),其黑高度为bh(x)。
对于节点x的左右子树,它们黑高度为 bh(x) 或者 bh(x)-1。
根据(02)的已知条件,我们已知 "x的左右子树,即高度为 h-1 的节点,它包含的节点至少为 2bh(x)-1-1 个";
所以,节点x所包含的节点至少为 ( 2bh(x)-1-1 ) + ( 2bh(x)-1-1 ) + 1 = 2^bh(x)-1。即节点x所包含的节点至少为 2bh(x)-1。
因此,原命题成立。
由(01)、(02)得出,"高度为h的红黑树,它的包含的内节点个数至少为 2^bh(x)-1个"。
因此,“一棵含有n个节点的红黑树的高度至多为2log(n+1)”。
3 手撕红黑树的左旋与右旋
左旋
void rbtree_left_rotate(rbtree *T, rbtree_node *x) {
rbtree_node *y = x->right; // x --> y , y --> x, right --> left, left --> right
x->right = y->left; //1 1
if (y->left != T->nil) { //1 2
y->left->parent = x;
}
y->parent = x->parent; //1 3
if (x->parent == T->nil) { //1 4
T->root = y;
} else if (x == x->parent->left) {
x->parent->left = y;
} else {
x->parent->right = y;
}
y->left = x; //1 5
x->parent = y; //1 6
}
右旋
void rbtree_right_rotate(rbtree *T, rbtree_node *y) {
rbtree_node *x = y->left;
y->left = x->right;
if (x->right != T->nil) {
x->right->parent = y;
}
x->parent = y->parent;
if (y->parent == T->nil) {
T->root = x;
} else if (y == y->parent->right) {
y->parent->right = x;
} else {
y->parent->left = x;
}
x->right = y;
y->parent = x;
}
4 红黑树添加的实现与添加三种情况
首先我们需要清楚一点,我们添加的那个节点首先把他设置成红色,为了满足上述红黑树的性质5
将节点和红黑树的其他节点作比较,插入在合适的位置上。分析插入后的节点情况。
情况一:插入节点的父节点是红色,父节点是祖父节点的左子树,叔父节点是红色的:
情况二:插入节点的父节点是红色,父节点是祖父节点的左子树,叔父节点是黑色的:
1 判断插入节点是不是父节点的右子树,如果是则需要左转操作,如果是父节点的左子树就不需要。将插入节点的父节点设置成黑色。祖父节点设置成红色,已插入节点的父节点的曾父节点威支点进行右旋。
情况三:
手撕这三种情况的代码:
void rbtree_insert_fixup(rbtree *T, rbtree_node *z) {
while (z->parent->color == RED) { //z ---> RED
if (z->parent == z->parent->parent->left) {
rbtree_node *y = z->parent->parent->right;
if (y->color == RED) {
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent; //z --> RED
} else {
if (z == z->parent->right) {
z = z->parent;
rbtree_left_rotate(T, z);
}
z->parent->color = BLACK;
z->parent->parent->color = RED;
rbtree_right_rotate(T, z->parent->parent);
}
}else {
rbtree_node *y = z->parent->parent->left;
if (y->color == RED) {
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent; //z --> RED
} else {
if (z == z->parent->left) {
z = z->parent;
rbtree_right_rotate(T, z);
}
z->parent->color = BLACK;
z->parent->parent->color = RED;
rbtree_left_rotate(T, z->parent->parent);
}
}
}
T->root->color = BLACK;
}
5 红黑树删除的实现与删除四种情况
手撕删除节点代码
rbtree_node *rbtree_delete(rbtree *T, rbtree_node *z) {
rbtree_node *y = T->nil;
rbtree_node *x = T->nil;
if ((z->left == T->nil) || (z->right == T->nil)) {
y = z;
} else {
y = rbtree_successor(T, z);
}
if (y->left != T->nil) {
x = y->left;
} else if (y->right != T->nil) {
x = y->right;
}
x->parent = y->parent;
if (y->parent == T->nil) {
T->root = x;
} else if (y == y->parent->left) {
y->parent->left = x;
} else {
y->parent->right = x;
}
if (y != z) {
z->key = y->key;
z->value = y->value;
}
if (y->color == BLACK) {
rbtree_delete_fixup(T, x);
}
return y;
}
void rbtree_delete_fixup(rbtree *T, rbtree_node *x) {
while ((x != T->root) && (x->color == BLACK)) {
if (x == x->parent->left) {
rbtree_node *w= x->parent->right;
if (w->color == RED) {
w->color = BLACK;
x->parent->color = RED;
rbtree_left_rotate(T, x->parent);
w = x->parent->right;
}
if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
w->color = RED;
x = x->parent;
} else {
if (w->right->color == BLACK) {
w->left->color = BLACK;
w->color = RED;
rbtree_right_rotate(T, w);
w = x->parent->right;
}
w->color = x->parent->color;
x->parent->color = BLACK;
w->right->color = BLACK;
rbtree_left_rotate(T, x->parent);
x = T->root;
}
} else {
rbtree_node *w = x->parent->left;
if (w->color == RED) {
w->color = BLACK;
x->parent->color = RED;
rbtree_right_rotate(T, x->parent);
w = x->parent->left;
}
if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
w->color = RED;
x = x->parent;
} else {
if (w->left->color == BLACK) {
w->right->color = BLACK;
w->color = RED;
rbtree_left_rotate(T, w);
w = x->parent->left;
}
w->color = x->parent->color;
x->parent->color = BLACK;
w->left->color = BLACK;
rbtree_right_rotate(T, x->parent);
x = T->root;
}
}
}
x->color = BLACK;
}
6 红黑树的线程安全的做法
在应用红黑树的时候,如果多个线程在操作红黑树时,需要考虑红黑树的线程安全问题。
考虑加锁,判断在加锁的情况下数据的处理是否复杂,如果复杂可以考虑应用粒度大的锁,互斥锁,如果数据处理不复杂,可以选用粒度较小的自旋锁。可以对整棵树加锁,也可以针对某个需要操作的子树进行加锁。
7 分析红黑树工程实用的特点
红黑树对于内存存储需要经常遍历来说,选用红黑树存储是可行的,其查找索引的时间复杂度是log2(n),方便遍历。