红黑树(rbtree) & ksvstore引擎(引擎层应用)

1.什么是红黑树?

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

2.红黑数的性质(重点)

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

3. 红黑树实现

3.1 红黑树定义

红黑树数据结构包括:

  1. 红黑树节点结构体:
    • 父节点: struct _rbtree_node *parent;
    • 左子树: struct _rbtree_node *left;
    • 右子树: struct _rbtree_node *right
    • 颜色: unsigned char color
    • key: KEY_TYPE key;
    • value: void *value
  2. 红黑树结构体:
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 左旋

由于右边比重较大,将右边节点向左平衡。

具体操作:

  1. 重置 x 的right :
    • 将y的left赋值给x的right :x的右子树指向y的左子树 x->right = y->left;
    • 将y的left的父节点指向x:y的左子树的父节点指向x y->left->parent = x;
  2. 重置 y 的parent :
    • 将x的parent赋值给y的parent:y的父结点指向x的父结点 y->parent = x->parent;
    • 将y赋值给x的parent的left或right:本来指向x结点的父指针,改成指向y x->parent->left/rigth = y;
  3. 重置 y 的left :
    • y的left指向x:y的左子树指向x结点 y->left = x;
    • x的parent指向y:x的父节点指向y x->parent = 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. 将当前节点重新指向爷节点(递归维护)
1.2 叔节点是黑色的,且新插入节点为左子树(L L b) --变色& 右旋
  1. 将父结点变成黑色,爷结点变成红色
  2. 以爷结点为中心右旋
1.3 叔节点是黑色的,且新插入节点为右子树(L R b) --变色& 左旋
  1. 以父结点为中心左旋
  2. 将父结点变黑色,爷结点变红色
  3. 以爷结点为中心右旋
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 增加(插入)

  1. 构造树节点 : 构造key和value,并malloc空间
  2. 调用红黑树的插入函数: 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 查询

对红黑树的遍历

  1. 调用红黑树的查询函数: 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等方法。

相关推荐
夜听莺儿鸣7 小时前
502-003_Linux 中断与异常(一):从Linux角度理解中断与异常
linux·中断与异常·linux中断
杨云龙UP7 小时前
TDengine 3.4.2.8 Community 三节点三副本生产集群部署实战(DNode/MNode/taosAdapter/Explorer)
大数据·linux·运维·数据库·tdengine·时序库
向成科技8 小时前
XC3576H工控主板|深度适配Ubuntu 26.04 LTS,释放边缘AI与工业开发新潜能
linux·人工智能·ubuntu·机器人·硬件·主板·边缘ai
Fcy6489 小时前
Linux下 进程间关系与守护进程
linux·运维·服务器·守护进程
码农小韩9 小时前
Linux驱动理论(二)——Linux字符设备驱动
linux·嵌入式软件开发·linux操作系统·linux应用开发·linux驱动理论
well06129 小时前
Linux粘滞位与Makefile机制深度解析
linux·运维·服务器
再写一行代码就下班9 小时前
linux sh脚本在windows修改导致无法使用解决方式
java·linux·centos
额额额对了10 小时前
SPI通信
linux·c语言·汇编·单片机·嵌入式硬件·arm
子木HAPPY阳VIP10 小时前
Ubuntu 关闭防火墙操作步骤
linux·运维·ubuntu
王振超wzc10 小时前
嵌入式开发环境搭建--WM软件安装,Ubuntu操作系统安装
linux·运维·ubuntu