1. 红黑树是什么
- 红黑树是一种特定类型的二叉树,用来组织数据的一种结构
- 红黑树是一种平衡二叉查找树的变体,左右子树高差有可能大于1,并不是严格意义上的平衡二叉树(AVL)
- 在对红黑树查找时,可以采用普通二叉排序树上的查找算法,在查找过程中不需要颜色信息
2. 红黑树用在哪里
- hashmap
- cfs
- epoll
- 定时器
- nginx
主要用法:
- 通过key去查找value
- 二叉排序树,是有一个有序的,当作顺序执行
因为key-value是一个强查找的过程,kv结构有下面这几种
- rbtree
- hash
- b/b+tree
- 跳表
3. 红黑树性质
- 每个结点是红的或者黑的
- 根结点是黑的
- 每个叶子结点是黑的
- 如果一个结点是红的,则它的两个儿子都是黑的
- 对每个结点,从该结点到其子孙结点到所有路径上的包含相同数目的黑结点(决定了高度相同)
#define RBTREE_ENTRY(name, type) \
struct name { \
struct type *right; \
struct type *left; \
struct type *parent;\
unsigned char color; \
}
typedef struct _rbtree_node{
KEY_TYPE key;
void *value;
#if 1
struct _rbtree_node *right;
struct _rbtree_node *left;
struct _rbtree_node *parent;
unsigned char color;
#else
RBTREE_ENTRY(, rb_node) node; // 把红黑树的性质封装一下, 会更灵活
#endif
} rbtree_node;
typedef struct _rbtree {
struct _rbtree_node *root;
struct _rbtree_node *nil;
} rbtree;
4. 旋转
- 红黑树性质被破坏的时候,才进行调整
- 红黑树最多的旋转次数就是层高
- 旋转是为了不影响其他结点,更好的去变色
// 左旋:要一个树, 和一个待旋转的结点
// 1. x指向y的左子树
// 2. y的左子树指向x
// 3. x的parent指向y
// 改变方向, 总共需要改动6个指针
void rbtree_left_rotate(rbtree *T, rbtree_node *x){
rbtree_node *y = x->right;
// 1. x指向y的左子树
x->right = y->left;
// 如果不是叶子结点, 才进行修改
if(y->left != T->nil){
y->left->parent = x;
}
// 2. y的左子树指向x
y->parent = x->parent;
// 需要判断一下x是不是根结点
if(x->parent == T->nil){
T->root = y;
}else if(x == x->parent->left){
x->parent->left = y;
}else{
x->parent->right = y;
}
// 3. x的parent指向y
y->left = x;
x->parent = y;
}
void rbtree_right_rotate(rbtree *T, rbtree_node *y){
rbtree_node *x = y->left;
// 1.
y->left = x->right;
// 如果不是叶子结点, 才进行修改
if(x->right != T->nil){
x->right->parent = y;
}
// 2.
x->parent = y->parent;
// 需要判断一下y是不是根结点
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. 颜色
红黑树在插入任何一个结点之前,他已经是一个红黑树
- 上红色,这个黑色的高度没有修改
- 如果父节点是红色,就需要调整
- 如果叔父结点是红色的
- 如果叔父结点是黑色的,而且当前结点是左孩子
- 这个时候需要右旋,因为左边比较重
- 父节点改成黑色,祖父结点改成红色,然后旋转
void rbtree_insert_fixup(rbtree *T, rbtree_node *z){
// z->color == RED
while(z->parent->color == 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在每一次循环回溯的时候,都是红色的
}else{ // y->color == BLACK
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);
}
}
}
}
void rbtree_insert(rbtree *T, rbtree_node *z){
rbtree_node *x = T->root;
rbtree_node *y = T->nil;
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;
}
}
// 如果这个红黑树一个结点都没有
if(y == T->nil){
T->root = z;
}else{
// z 插到 y 的左子树还是右子树 ?
if(y->key > z->key){
y->left = z;
}else{
y->right = z;
}
}
z->parent = y;
z->left = T->nil;
z->right = T->nil;
z->color = RED;
}
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:
Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习