红黑树(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的parentleftright:本来指向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中引用红黑树引擎已经实现,其核心思想就是在getsetdel等封装的引擎方法中调用红黑树内部的insertdeletsearch等方法。

相关推荐
智能工业品检测-奇妙智能2 小时前
linux 安装 FFmpeg 和windows安装 FFmpeg
linux·windows·ffmpeg
那我掉的头发算什么2 小时前
【Linux】Linux基本使用和程序部署
linux·运维·服务器·数据库·spring·mybatis
咋吃都不胖lyh4 小时前
linux环境在vscode链接到一个git仓库,克隆和拉取详细命令行
linux·git·vscode
小小小米粒6 小时前
NAT 模式, 仅主机模式 Host-Only ,桥接模式 Bridge
linux·服务器·网络
沙漏无语10 小时前
(二)TIDB搭建正式集群
linux·数据库·tidb
思麟呀10 小时前
计算机网络初步认识
linux·计算机网络
浩子智控10 小时前
zynq嵌入式开发(2)—基本开发测试实例
linux·嵌入式硬件·硬件架构
闻道且行之10 小时前
Nginx 安装、做成服务及 HTTPS 配置全流程
linux·运维·nginx·https
昵称只能一个月修改一次。。。10 小时前
Linux系统编程:网络编程
linux·服务器·网络