红黑树的五个特性如下:
- 红黑树是一棵平衡二叉树
- 每个节点非红即黑
- 根节点是黑色
- 红节点的子节点必为黑色
- 任一节点到其各个叶子节点所经过的黑色节点是相等的
nginx中的红黑树数据结构如下:
struct ngx_rbtree_s{
ngx_rbtree_node_t *root;//根节点指针
ngx_rbtree_node_t *sentinel;//叶子节点
ngx_rbtree_insert_pt insert;//插入方法
};// 这是红黑树的结构
struct ngx_rbtree_node_s{
ngx_rbtree_key_t key; /* 节点的键值 */
ngx_rbtree_node_t *left; /* 节点的左孩子 */
ngx_rbtree_node_t *right; /* 节点的右孩子 */
ngx_rbtree_node_t *parent; /* 节点的父亲 */
u_char color; /* 节点的颜色 */
u_char data;
};
红黑树的插入通常会以平衡二叉树的方式先插入,然后再对节点造成的不平衡进行处理.
void
ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)
{
ngx_rbtree_node_t **root, *temp, *sentinel;
root = (ngx_rbtree_node_t **) &tree->root;
sentinel = tree->sentinel;
if(*root == sentinel){//情况1,空树插入节点,只将节点染成黑色即可
node->parent = NULL;
node->left = sentinel;
node->right = sentinel;
ngx_rbt_black(node);
*root = node;//将树的根指向新节点
return;
}
tree->insert(*root, sentinel, node);//调用自定义的插入节点方法
/*新插入的节点为红色,如果父节点也是红色,则平衡被破坏,需要处理*/
while(node != *root && ngx_rbt_is_red(node->parent)){
//先考虑新增节点的父节点是祖父节点左子树的情况
if(node->parent == node->parent->parent->left){
temp = node->parent->parent->right;//树父节点
if(ngx_rbt_is_red(temp)){
/*如果叔父节点是红色,说明叔父节点与父节点的平衡因子差1,
原则上符合avl树的平衡规则,则
将父节点和叔父节点都染成黑色,将祖父节点染红,此时祖父节点破坏了红黑树的平衡,
再以祖父节点进行平衡处理*/
ngx_rbt_black(node->parent);
ngx_rbt_black(temp);
ngx_rbt_red(node->parent->parent);
node = node->parent->parent;
}else{
if(node == node->parent->right){
/*如果插入节点是一个右节点,先对父节点进行左旋处理*/
node = node->parent;
ngx_rbtree_left_rotate(root, sentinel, node);
}
//如果叔父节点是黑色或者是一个叶子,则说明父节点和叔父节点的平衡因子大于1,
//失去平衡,则需要对祖父节点进行右旋,先将父节点染黑,将祖父节点染红,对祖父节点右旋
ngx_rbt_black(node->parent);
ngx_rbt_red(node->parent->parent);
ngx_rbtree_right_rotate(root, sentinel, node->parent->parent);
}
}else{
//以下与上面是对称的,
temp = node->parent->parent->left;
if (ngx_rbt_is_red(temp)) {
ngx_rbt_black(node->parent);
ngx_rbt_black(temp);
ngx_rbt_red(node->parent->parent);
node = node->parent->parent;
} else {
if (node == node->parent->left) {
node = node->parent;
ngx_rbtree_right_rotate(root, sentinel, node);
}
ngx_rbt_black(node->parent);
ngx_rbt_red(node->parent->parent);
ngx_rbtree_left_rotate(root, sentinel, node->parent->parent);
}
}
}
ngx_rbt_black(*root);
}
平衡树的左旋操作:
将当前节点的右节点作为当前节点的父节点,将当前节点右节点的左节点作为当前节点的右节点;
static ngx_inline void
ngx_rbtree_left_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,
ngx_rbtree_node_t *node){
ngx_rbtree_node_t *temp;
temp = node->right;//先定位到右节点
node->right = temp->left;//将右节点的左节点赋给当前节点的右节点
if(temp->left != sentinel){//如果被移动的节点不是一个叶子,则需要将父指针赋值
temp->left->parent = node;
}
temp->parent = node->parent;//改变右节点的父指针
if(node == *root){//如果是对根进行左旋,则需要修改根指针
*root = temp;
}else if (node == node->parent->left) {//对父级节点的左右指针进行修改
node->parent->left = temp;
} else {
node->parent->right = temp;
}
temp->left = node;//最后将当前节点赋给其右节点的左节点,完成左旋
node->parent = temp;//修改父指针指向
}
平衡树的右旋操作:
右旋是将当前节点的左节点的右节点赋给当前节点的右节点,将当前节点赋给当前节点左节点的右节点.
static ngx_inline void
ngx_rbtree_right_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,
ngx_rbtree_node_t *node){
ngx_rbtree_node_t *temp;
temp = node->left;//先定位左节点
node->left = temp->right;//将左节点的右节点赋给当前节点的左节点
if(temp->right != sentinel){
temp->right->parent = node;
}
temp->parent = node->parent;//修改左节点的父指针
/*修改祖父节点的左指针或右指针*/
if (node == *root) {
*root = temp;
} else if (node == node->parent->right) {
node->parent->right = temp;
} else {
node->parent->left = temp;
}
temp->right = node;//最后将当前节点赋给其左节点的右节点
node->parent = temp;//修改当前节点的父节点为其左节点,旋转完成
}
红黑树的删除:
void
ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)
{
ngx_uint_t red;
ngx_rbtree_node_t **root, *sentinel, *subst, *temp, *w;
root = (ngx_rbtree_node_t **)&tree->root;
sentinel = tree->sentinel;
if(node->left == sentinel){
temp = node->right;
subst = node;
} else if (node->right == sentinel) {
temp = node->left;
subst = node;
} else{
subst = ngx_rbtree_min(node->right, sentinel);
if(subst->left != sentinel){
temp = subst->left;
}else{
temp = subst->right;
}
}
if(subst == *root){
*root = temp;
ngx_rbt_black(temp);
node->left = NULL;
node->right = NULL;
node->parent = NULL;
node->key = 0;
return;
}
red = ngx_rbt_is_red(subst);
if(subst == subst->parent->left){
subst->parent->left = temp;
}else{
subst->parent->right = temp;
}
}