红黑树的来龙去脉
二叉搜索树(BST):对于任意节点,左子树的值必定小于该节点,右子树的值必定大于该节点。中序遍历,就是一个有序遍历。增加、删除、修改,查找的时间复杂度都是O(log(N))。
缺点:容易退化成一个链表,极端情况下时间复杂度变成O(N)。二叉树依次插入 【1,2,3,4,5,6】
平衡二叉搜索树(AVL): 左右子树高度差绝对值不超过1,并且左右子树都是一颗平衡二叉树。二叉树插入 【1,2,3,4,5,6,7】
可以看到AVL没有二叉搜索树的缺点,左右子树高度差绝对值不超过1。但是AVL Tree的插入、删除、修改都需要通过旋转维护整个树的平衡。
-
AVL查询:O(log(N))
-
AVL插入: 旋转一次,查询时间O(log(N)),插入复杂度O(log(N))
-
AVL删除: 必须检查从删除节点开始到根节点路径上的所有节点平衡因子。删除最多需要log(N)旋转,加上查询时间,删除复杂度O(2log(N)
红黑树(RBT): AVL的增删改,需要保持AVL的严格平衡,时间复杂度比较高,所以提出红黑树。
-
rbTree查询元素:O(log(N))
-
rbTree插入元素:插入最多2次旋转,加上查询的时间O(log(N)),插入的复杂度O(log(N))
-
rbTree删除元素:删除最多需要3次旋转,加上查询的时间,删除的复杂度O(log(N)
红黑树相比AVL树
- 红黑树的查询效率略低于AVL的查询
- 红黑树和AVL插入的速度差不多
- 红黑树删除的速度比AVL快,因为AVL删除最多需要log(N)次旋转
红黑树的应用场景
1. C++ STL库map,set使用红黑树封装;
2. 进程调度实体采用红黑树组织,CFS通过vruntime作为红黑树的key,struct_entity作为value;
进程主要包含:1. 进程描述;2. 进程调度实体;3进程调度策略。
3. 内存管理(每次使用malloc的时候会调用brk系统调用分配一块内存(固定大小一般一个页大小),这块内存就是用红黑树来组织,在vm_area_struct 使用vm_start,vm_end描述这块内存块,所以key->开始地址,val->大小)
4. epoll中使用红黑树管理所有IO,用户程序调用epoll_ctl来增删改红黑树节点,当有数据就绪后报给用户程序;
5. sk_buff 从网卡接收数据到协议栈或从协议栈发送数据到网卡,一帧数据使用sk_buff来描述,红黑树用来描述各帧数据之间的关系,一般在ip分片和tcp协议栈中使用;
6. nginx中使用红黑树管理定时器,中序遍历第一个就是最小的定时器;
红黑树的性质
1. 根节点是黑色的;
2. 所有节点不是红色就是黑色;
3. 如果一个节点是红色的,那么此节点的两个孩子节点必定是黑色的;不能出现两个红色节点相邻
4. 从任一节点到叶子节点,所有黑色节点的个数相同;
5. 叶子节点是黑色的;默认都隐藏叶子结点
综上红黑树的5个性质,从3和4可以推断出红黑树的最长路径不超过最短路径的2倍。
最长的一条路径:红黑交替;
最短的一条路径:全是黑色;
红黑树实现原理
插入节点颜色选择及红黑树平衡:
1. 插入根节点,节点颜色为黑色即可,然后完成插入;满足性质1;
2. 默认插入孩子节点颜色为红色;满足性质4
3. 插入孩子节点颜色为红色,如果父节点是黑色,任务完成。
4. 插入孩子节点,如果父节点是红色,性质3不满足,依据叔叔节点继续判断。
4.1 如果叔叔节点也是红色
将父节点与叔叔节点变为黑色,祖父节点变为红色,然后以祖父节点为新插入节点继续向上判断。变色后叔叔节点到叶子节点的路径和父亲节点到叶子节点的路径黑高不变。
红黑树变色前 红黑树变色后
4.2 向上判断过程中如果叔叔节点是黑色或者没有叔叔节点
向上判断过程中,当前节点必定是红色的。可能会发生不满足红黑树特性的情况,只能通过旋转+变色来满足。分四种情况描述
4.2.1 当前节点为父节点左子树,且在根节点左侧
父节点右旋转,父节点变黑,祖父变红。
4.2.2 当前节点为父节点右子树,且在根节点右侧
父节点左旋转,父节点变黑,祖父节点变红
4.2.3 当前节点为父节点右子树,且在根节点左侧
cur节点左旋转后右旋转,插入节点变黑,祖父节点变红
4.2.4 当前节点为父节点左子树,且在根节点右侧
cur节点右旋转后左旋转,插入节点变黑,祖父节点变红
红黑树原理总结
红黑树左旋右旋实现
x的左旋和y的右旋是一个可逆的过程,代码见实现部分
红黑树插入节点
插入节点前默认是满足红黑树要求的,插入节点后需要维护红黑树,代码见实现部分
如果父结点是爷结点是左子树,可以归纳出来三种情况。同理如果父结点是爷结点是右子树,我们也可以归纳出来三种情况。在下面的图中:z表示新增结点,y表示叔父结点。
父节点是祖父节点左子树
1. 叔父节点是红色的
将叔父节点和父节点变黑,祖父节点变红;
将当前节点变成祖父节点。
2. 叔父节点是黑色的且新节点是左子树
将父节点变成黑色,祖父节点变成红色;
以祖父节点为中心右旋;
3. 叔父节点是黑色的且新节点是右子树
以父节点为中心左旋
将父节点变成黑色,祖父节点变成红色
以祖父节点为中心右旋
父节点是祖父节点右子树
也是分三种情况和左子树类似,此处省略号123。
红黑树删除节点
假设我们自定义节点
- 覆盖结点:z(被指定删除的结点,实际上被覆盖)
- 删除结点:y(实际上被删除的结点,一般是后继结点)
- 轴心结点:x(维护红黑树的结点)
红黑树删除节点根据左右子树划分
1. 没有左右子树:直接删除该节点;
2. 只有一个子树:将该节点唯一子树挂到父节点,然后删除该节点;
3. 左右子树均有:找一个删除节点y覆盖指定节点z,删除删除节点y,对于删除节点y,一定是1或者2
删除节点维护红黑树
如果删除节点是红色,直接喊出即可;
如果删除节点是黑色有几种情况:
如果当前结点是父结点的左子树的情况,可以归纳出来四种情况。同理如果当前结点是父结点的右子树,我们也可以归纳出来四种情况。
当前节点是父节点左子树的情况
1. 当前节点的兄弟节点是红色
- 兄弟节点变成黑色
- 父节点变成红色
- 父节点做左旋
- 将兄弟结点调整为父节点的右子树
2. 当前节点的兄弟节点是黑色的,并且兄弟节点的两个孩子节点都是黑色的
- 兄弟节点变成红色(保持和父节点删除后黑高相同)
- 轴心结点变为父节点
3. 当前节点的兄弟节点是黑色的,而且兄弟节点的左孩子是红色的,右孩子是黑色的。
-
将左孩子涂黑
-
将兄弟节点变红
-
对兄弟节点右旋
-
将兄弟结点调整为父节点的右子树
4. 当前节点的兄弟节点是黑色的,而且兄弟节点的左孩子是黑色的,右孩子是红色的。
-
将兄弟节点换成父节点的颜色
- 把父节点和兄弟节点的右孩子涂黑
- 对父节点做左旋
- 设置x指针,指向根节点
当前节点是父节点右子树的情况
和左子树情况类似
红黑树代码实现
/*
红黑树的实现:
1. Insert;
2. Delete;
3. Traversal;
4. Search/update
*/
/* 0. 插入根,节点为黑色即可;
1. 插入父节点是黑色,插入红色节点即可
2. 插入父节点是红色,叔父节点也是红色;将父节点和叔父节点变为黑色,祖父节点变为红色,以祖父节点为新插入节点向上判断
3. 插入父节点红色,叔父节点为黑色或没有叔父节点,说明插入前已经不是红黑树,此情况出现在组父节点向上判断
4. 此时靠旋转+变色来解决包含两种单旋,两种双旋
5. A.当前节点为父节点左子树,且在根节点左侧->父节点右旋,父节点变黑,组父节点变红
B.当前节点为父节点右子树,且在根节点右侧->父节点左旋,父节点变黑,组父节点变红
C.当前节点为父节点右子树,且在根节点左侧->当前节点左旋转后右旋转,插入节点变黑,组父节点变红
D.当前节点为父节点左子树,且在根节点右侧->当前节点右旋转后左旋转,插入节点变黑,祖父节点变红*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define RED 1
#define BLACK 2
typedef int KEY_TYPE;
typedef struct _rbtree_node {
unsigned char color;
struct _rbtree_node *right;
struct _rbtree_node *left;
struct _rbtree_node *parent;
KEY_TYPE key;
void *value;
} rbtree_node;
typedef struct _rbtree {
rbtree_node *root;
rbtree_node *nil;
} rbtree;
rbtree_node *rbtree_mini(rbtree *T, rbtree_node *x) {
while (x->left != T->nil) {
x = x->left;
}
return x;
}
rbtree_node *rbtree_maxi(rbtree *T, rbtree_node *x) {
while (x->right != T->nil) {
x = x->right;
}
return x;
}
rbtree_node *rbtree_successor(rbtree *T, rbtree_node *x) {
rbtree_node *y = x->parent;
if (x->right != T->nil) {
return rbtree_mini(T, x->right);
}
while ((y != T->nil) && (x == y->right)) {
x = y;
y = y->parent;
}
return y;
}
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;
}
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;
}
void rbtree_insert(rbtree *T, rbtree_node *z) {
rbtree_node *y = T->nil;
rbtree_node *x = T->root;
while (x != T->nil) {
y = x;
if (z->key < x->key) {
x = x->left;
} else if (z->key > x->key) {
x = x->right;
} else { //Exist
return ;
}
}
z->parent = y;
if (y == T->nil) {
T->root = z;
} else if (z->key < y->key) {
y->left = z;
} else {
y->right = z;
}
z->left = T->nil;
z->right = T->nil;
z->color = RED;
rbtree_insert_fixup(T, z);
}
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;
}
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;
}
rbtree_node *rbtree_search(rbtree *T, KEY_TYPE key) {
rbtree_node *node = T->root;
while (node != T->nil) {
if (key < node->key) {
node = node->left;
} else if (key > node->key) {
node = node->right;
} else {
return node;
}
}
return T->nil;
}
void rbtree_traversal(rbtree *T, rbtree_node *node) {
if (node != T->nil) {
rbtree_traversal(T, node->left);
printf("key:%d, color:%d\n", node->key, node->color);
rbtree_traversal(T, node->right);
}
}
int main() {
int keyArray[20] = {24,25,13,35,23, 26,67,47,38,98, 20,19,17,49,12, 21,9,18,14,15};
rbtree *T = (rbtree *)malloc(sizeof(rbtree));
if (T == NULL) {
printf("malloc failed\n");
return -1;
}
T->nil = (rbtree_node*)malloc(sizeof(rbtree_node));
T->nil->color = BLACK;
T->root = T->nil;
rbtree_node *node = T->nil;
int i = 0;
for (i = 0;i < 20;i ++) {
node = (rbtree_node*)malloc(sizeof(rbtree_node));
node->key = keyArray[i];
node->value = NULL;
rbtree_insert(T, node);
}
rbtree_traversal(T, T->root);
printf("----------------------------------------\n");
for (i = 0;i < 20;i ++) {
rbtree_node *node = rbtree_search(T, keyArray[i]);
rbtree_node *cur = rbtree_delete(T, node);
free(cur);
rbtree_traversal(T, T->root);
printf("----------------------------------------\n");
}
}