1.什么是红黑树?
红黑树作为特殊的二叉平衡树,其存储数据的直观特点是矮胖型。该数据结构的CRUD特性:

2.红黑数的性质(重点)
- 每个节点都是红色或黑色
- 根叶黑: 根节点和叶子节点全是黑色
- 黑高同: 从头结点到任意叶子节点所含的黑色节点数量相同
- 不红红: 不能出现两个红色节点作为父子,一个红色节点 其父子都不能为红色节点

3. 红黑树实现
3.1 红黑树定义
红黑树数据结构包括:
- 红黑树节点结构体:
- 父节点:
struct _rbtree_node *parent; - 左子树:
struct _rbtree_node *left; - 右子树:
struct _rbtree_node *right - 颜色:
unsigned char color - key:
KEY_TYPE key; - value:
void *value
- 父节点:
- 红黑树结构体:
cpp
#define RED 0
#define BlACK 1
typedef int KEY_TYPE;
typedef struct _rbtree_node {
unsigned char color;//颜色
struct _rbtree_node *left;//左子树
struct _rbtree_node *right;//右子树
struct _rbtree_node *parent;//父结点
KEY_TYPE key;
void *value;
} rbtree_node;//红黑树结点
typedef struct _rbtree {
rbtree_node *root;//根结点
rbtree_node *nil;//通用叶子结点
} rbtree;//红黑树
3.2 红黑数的左旋和右旋
红黑树为了平衡自身的数据,防止退化为链表结构,尽可能的会维持自身的矮胖结构,所以通过左旋和右旋达到这种目的。
左旋和右旋都是改变3个方向,重置6个指针

3.2.1 左旋

由于右边比重较大,将右边节点向左平衡。
具体操作:
- 重置 x 的
right:- 将y的
left赋值给x的right:x的右子树指向y的左子树x->right = y->left; - 将y的
left的父节点指向x:y的左子树的父节点指向xy->left->parent = x;
- 将y的
- 重置 y 的
parent:- 将x的
parent赋值给y的parent:y的父结点指向x的父结点y->parent = x->parent; - 将y赋值给x的
parent的left或right:本来指向x结点的父指针,改成指向yx->parent->left/rigth = y;
- 将x的
- 重置 y 的
left:- y的
left指向x:y的左子树指向x结点y->left = x; - x的
parent指向y:x的父节点指向yx->parent = y;
- y的
3.2.2 右旋

同理和上面的左旋 xy调换,left和right调换即可。
3.2.3左旋右旋具体代码实现:
cpp
//左旋leftRotate(T,x)---中右->左中
//降低X结点的高度,提高X结点右结点(即Y)的高度。
void _left_rotate(rbtree *T, rbtree_node *x) {
rbtree_node *y = x->right;
//1
x->right = y->left;//x的右子树指向y的左子树
if (y->left != T->nil) {
y->left->parent = x;//y的左子树的父节点指向x
}
//2
y->parent = x->parent;//y的父结点指向x的父结点
if (x->parent == T->nil) {//如果x是根结点
T->root = y;
} else if (x == x->parent->left) {
x->parent->left = y;//本来指向x结点的父指针,改成指向y
} else {
x->parent->right = y;
}
//3
y->left = x;//y的左子树指向x结点
x->parent = y;//x的父节点指向y
}
//右旋
//copy左旋的代码
//left改成right,right改成left
//x改成y,y改成x
void _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;
if (y->parent == T->nil) {
T->root = x;
} else if (y == y->parent->right) {
y->parent->right = x;
} else {
y->parent->left = x;
}
//3
x->right = y;
y->parent = x;
}
3.3 红黑数的插入
3.3.1 快速理解


3.3.2 插入(不含旋转维护)
此段代码只是根据大小遍历红黑树,然后找到插入位置,将 目标节点插入叶子结点上。
cpp
//因为传入的key可能是字符,可能是整形,所以要提取出来
//这里可以看出,其实可以封装成一个模板
int key_compare(KEY_TYPE a, KEY_TYPE b) {
//这里假设是int
if (a > b) {
return 1;
} else if (a < b) {
return -1;
} else {
return 0;
}
}
void rbtree_insert(rbtree *T, rbtree_node *z) {
//找位置
rbtree_node *x = T->root;
rbtree_node *y = T->nil;//y是x的父节点
while (x != T->nil) {//二分找位置
y = x;
if (key_compare(z->key, x->key) < 0) {
x = x->left;
} else if (key_compare(z->key, x->key) > 0) {
x = x->right;
} else {
//如果key相等,看自己的业务情景
//重复插入可以不修改直接退出,可以修改val
return;
}
}
//插入
z->parent = y;
if (y == T->nil) {
T->root = z;
} else if (key_compare(z->key, y->key) < 0) {
y->left = z;
} else {
y->right = z;
}
z->left = T->nil;
z->right = T->nil;
z->color = RED;
//维护红黑树
rbtree_insert_fixup(T, z);
}
**rbtree_insert_fixup(T, z); ** :是核心重点函数,依据红黑树的规则进行递归调整。
3.3.3 插入后的旋转维护
1. 父节点是爷节点的左子树(L xx)
1.1 叔节点是红色的(L Xr) --只变色
- 叔节点和父节点变黑,爷节点边红
- 将当前节点重新指向爷节点(递归维护)

1.2 叔节点是黑色的,且新插入节点为左子树(L L b) --变色& 右旋
- 将父结点变成黑色,爷结点变成红色
- 以爷结点为中心右旋

1.3 叔节点是黑色的,且新插入节点为右子树(L R b) --变色& 左旋
- 以父结点为中心左旋
- 将父结点变黑色,爷结点变红色
- 以爷结点为中心右旋

2. 父节点是爷节点的右子树(R xx)
3.3.4 具体代码
cpp
//修复第4条性质
void rbtree_insert_fixup(rbtree *T, rbtree_node *z) {
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;
} 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;
} 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;
}
4. 在kvstore中的具体应用
4.1 创建红黑树引擎
cpp
//声明一个全局的红黑树结构
typedef struct _rbtree kvs_rbtree_t;
kvs_rbtree_t global_rbtree;
//对该树结构分配内存地址
int kvs_rbtree_create(kvs_rbtree_t *inst) {
if (inst == NULL) return 1;
inst->nil = (rbtree_node*)kvs_malloc(sizeof(rbtree_node));
inst->nil->color = BLACK;
inst->root = inst->nil;
return 0;
}
4.2 增加(插入)
- 构造树节点 : 构造key和value,并
malloc空间 - 调用红黑树的插入函数:
rbtree_insert(inst, node);
cpp
int kvs_rbtree_set(kvs_rbtree_t *inst, char *key, char *value) {
if (!inst || !key || !value) return -1;
rbtree_node *node = (rbtree_node*)kvs_malloc(sizeof(rbtree_node));
node->key = kvs_malloc(strlen(key) + 1);
if (!node->key) return -2;
memset(node->key, 0, strlen(key) + 1);
strcpy(node->key, key);
node->value = kvs_malloc(strlen(value) + 1);
if (!node->value) return -2;
memset(node->value, 0, strlen(value) + 1);
strcpy(node->value, value);
rbtree_insert(inst, node);
return 0;
}
4.3 查询
对红黑树的遍历
- 调用红黑树的查询函数:
rbtree_search(inst, key);
cpp
char* kvs_rbtree_get(kvs_rbtree_t *inst, char *key) {
if (!inst || !key) return NULL;
rbtree_node *node = rbtree_search(inst, key);
if (!node) return NULL; // no exist
if (node == inst->nil) return NULL;
return node->value;
}
4.4 删除 (较为复杂)
调用红黑树的删除节点函数 :rbtree_delete(inst, node)
cpp
int kvs_rbtree_del(kvs_rbtree_t *inst, char *key) {
if (!inst || !key) return -1;
rbtree_node *node = rbtree_search(inst, key);
if (!node) return 1; // no exist
rbtree_node *cur = rbtree_delete(inst, node);
free(cur);
return 0;
}
总结:
截止到此,kvstore中引用红黑树引擎已经实现,其核心思想就是在get、set、del等封装的引擎方法中调用红黑树内部的insert、delet、search等方法。