文章目录
1.红黑树工程实用的特点
- 查找效率高:通过key可以找到与之对应的value,查找的时间复杂度为
O(log~2~N)
.比如:epoll中对事件管理是通过红黑树来管理的,通过查找fd可以快速的找到与之对应的事件。与之相似的强查找过程的数据结构还有:rbtree、hashmap(时间复杂度O(1),效率最高)、B\B+树、跳表等。 - 中序遍历是有序的,可以用于筛选:比如:Timer定时器中判断定时器是否超时,在红黑树中找到一个小于或等于超时时间的定时器节点,那么这个节点左子树上的所有定时器节点都是超时的。
2.红黑树的应用场景(进程调度CFS、内存管理)
2.1 Linux进程调度CFS
2.2 Nginx Timer 事件管理
2.3 Epoll 事件块管理
3.红黑树的定义
判别一棵树是否为黑红黑树,要满足以下特点:
- 每个节点是红的或者黑的
- 根节点是黑的
- 每个叶子节点是黑的
- 如果一个节点是红的,则它的两个儿子都是黑的
- 对于每个节点,从该节点到其子孙节点的所有路径上的包含相同数目的黑节点
注意所有的叶子节点都是隐藏的,并且为黑色,在编程的时候为了方便管理所有的叶子节点,可以将所有的叶子节点指向同一个为空的节点,上图中只有图2满足红黑树的所有特点。
4.红黑树的左旋和右旋
旋转:当红黑树的性质被破坏的时候,此时需要对红黑树进行调整。对红黑树的调整包括:旋转与变色,左旋与右旋是一个可逆的过程。
左旋与右旋代码实现:
//b节点有两种情况:1.b为叶子节点 2.b不是叶子节点
//x有三种情况:1.x为根节点 2.x为x->parent的左孩子 3.x为x->parent的右孩子
void rbtree_left_rotate(rbtree *T, rbtree_node *x) {
rbtree_node *y = x->right;
x->right = y->left;
if(y->left != T->nil) //b不是叶子节点
y->left->parent = x;
//x有三种情况:
y->parent = x->parent;
if(x->parent == T->nil){
//x为根节点
T->root = y;
}else if(T->left = x){
//x为x->parent的左孩子
x->parent->left = y;
}else{
//x为x->parent的右孩子
x->parent->right = y;
}
x->parent = y;
y->left = x;
}
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;
}
5.红黑树添加一个新节点的三种情况
要添加一个新节点,新节点的初始颜色置为红色还是黑色? ** 红色**
插入的新节点是颜色是红色的好处:
1. 不会改变黑高,如果插入新节点的父节点的颜色为黑色,直接插入即可,不需要对红黑树进行调整。
2. 判断红黑树是否需要调整只需要判断新节点z
的父节点的颜色是否为红色即可。如果需要调整能够推测出:z
的颜色是红色的、z
的父节点的颜色是红色的、z
的祖父节点的颜色是黑色的、z
的叔父节点的颜色不确定。可以是红色的也可以是黑色的,这个是对后面红黑树调整的判断条件。
5.1 父节点是祖父节点的左子树的情况
插入一个新节点首先需要遍历红黑树,查找一个合适的插入位置,并且插入的新节点的颜色是红色的。如果破坏了红黑树的特性在对红黑树进行调整,下面是在红黑树找到一个合适的节点插入新节点的代码:
void rbtree_insert(rbtree *T, rbtree_node *z) {
retree_node *x = T->root;
retree_node *y = T->root;
//找到将要插入新节点z的父节点y
while(x == T->nil){
y = x;
if(z->key < x->key){
x = x->left;
}else{
x = x->right;
}
}
z->parent = y;
//判断z是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;
e->color = RED;
//对红黑树进行调整
rbtree_insert_fixup(T, z);
}
5.1.1 叔节点是红色的
插入的新节点z
的叔父节点是红色的,对红黑树进行调整只需要将z
的父节点与叔父节点的颜色由红色变成黑色,将z
的祖父节点的颜色由黑色变成红色,最后,再将z
的祖父节点赋值给z
。
5.1.2 叔节点是黑色的,而且当前节点是右孩子
这是由叔父节点是红色调整之后得到的新情况:叔父节点是黑色的,而且当前节点是右孩子。需要对红黑树进行调整:先将z
父节点赋值给z
,再对z
进行左旋,可以得到下一种情况:叔父节点是黑色的,而且当前节点是右孩子。
5.1.3 叔节点是黑色的,而且当前节点是左孩子
这是由树父节点的黑色的,而且当前节点是右孩子,经过左旋得到的新情况。需要对红黑树进行调整:变色:将z
的父节点的颜色由红色变成黑色,将z
的祖父节点有黑色变成红色。旋转:以z
的祖父节点进行右旋。
void rbtree_insert_fixup(rbtree *T, rbtree_node *z