红黑树上每个节点内含五个域,color,key,left,right,p。如果相应的指针域没有,则设为NIL。
叶子节点即为树尾端的NIL指针,或者说NULL节点。
红黑树需要遵从下面的5条性质:
(1)节点要么是红色要么是黑色;
(2)根节点为黑色;
(3)叶子节点即NIL节点必定为黑色;
(4)红色节点的孩子节点必定为黑色;
(5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点,即黑高度相同;
上面的5条规则,主要是第(4)、(5)两条保证了红黑树的近似完整平衡性。
红黑树的旋转操作
(1)右旋转
(2)左旋转
(3)先左旋再右旋
(4)先右旋再左旋
nginx相关内存
nginx实现的红黑树,包含两个结构体:ngx_rbtree_t和ngx_rbtree_node_t。
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;//节点颜色,0表示黑色,1表示红色
u_char data;//数据
};
typedef struct
{
ngx_rbtree_node_tnode;
ngx_int_t num;
void *data; //存储数据的指针
}TestRBTNode;//自定义的数据结构体必须包含ngx_rbtree_node_t
//向红黑树中插入节点。
void ngx_rbtree_insert(ngx_thread_volatilengx_rbtree_t *tree, ngx_rbtree_node_t *node);
//在红黑树中删除节点。
void ngx_rbtree_insert_value(ngx_rbtree_node_t*temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)。
插入删除例子
由于ngx_rbtree_t未牵涉到内存池,所以非常容易抽出来使用,如下为实现了插入、打印最小值、删除的例子
#include <iostream>
#include <algorithm>
#include <pthread.h>
#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "ngx_queue.h"
#include "ngx_rbtree.h"
int main()
{
ngx_rbtree_t tree;
ngx_rbtree_node_t sentinel;
ngx_rbtree_init(&tree,&sentinel,ngx_rbtree_insert_value);
ngx_rbtree_node_t *rbnode = new ngx_rbtree_node_t[100];
for(int i = 99; i >= 0 ;i--)
{
rbnode[i].key = i;
rbnode[i].parent = NULL;
rbnode[i].left = NULL;
rbnode[i].right = NULL;
ngx_rbtree_insert(&tree,&rbnode[i]);
}
for(int i = 0; i < 100;i++)
{
ngx_rbtree_node_t *p = ngx_rbtree_min(tree.root,&sentinel);
std::cout << p->key << " ";
ngx_rbtree_delete(&tree,p);
}
delete[] rbnode;
return 0;
}
添加遍历例子
#include <stdio.h>
#include <stdlib.h>
#include <ngx_core.h>
#include <ngx_config.h>
#include <ngx_conf_file.h>
#include <ngx_string.h>
#include <ngx_palloc.h>
#include <ngx_rbtree.h>
#define ngx_rbtee_data(p, type, member)(type*)((u_char*)p - offset(type, member))
//节点数据结构
typedef struct
{
ngx_rbtree_node_t node;//方便类型转化
ngx_str_t str;
}ngx_string_node_t;
//自定义插入函数
void ngx_string_rbtree_insert_value(ngx_rbtree_node_t*temp, ngx_rbtree_node_t *node,
ngx_rbtree_node_t*sentinel);
//先序遍历红黑树
void trave_rbtree(ngx_rbtree_node_t *root,ngx_rbtree_node_t *sentinel);
int main()
{
ngx_rbtree_t tree;
ngx_rbtree_node_tsentinel;
ngx_rbtree_init(&tree,&sentinel, ngx_string_rbtree_insert_value);
ngx_string_node_tstrNode[10];
ngx_str_set(&strNode[0].str,"abc0");
strNode[0].node.key= 1;
ngx_str_set(&strNode[1].str,"abc1");
strNode[1].node.key= 6;
ngx_str_set(&strNode[2].str,"abc2");
strNode[2].node.key= 8;
ngx_str_set(&strNode[3].str,"abc35");
strNode[3].node.key= 11;
ngx_str_set(&strNode[4].str,"abd4");
strNode[4].node.key= 8;
ngx_str_set(&strNode[5].str,"abc5");
strNode[5].node.key= 1;
ngx_str_set(&strNode[6].str,"abc11");
strNode[6].node.key= 11;
ngx_str_set(&strNode[7].str,"a6");
strNode[7].node.key= 1;
ngx_str_set(&strNode[8].str,"a8");
strNode[8].node.key= 6;
ngx_str_set(&strNode[9].str,"abc0");
strNode[9].node.key= 6;
ngx_int_t i;
for (i = 0; i< 10; ++i)
{
ngx_rbtree_insert(&tree,&strNode[i].node);
}
travel_rbtree(tree.root,tree.sentinel);
return 0;
}
void
ngx_string_rbtree_insert_value(ngx_rbtree_node_t *temp,ngx_rbtree_node_t *node,
ngx_rbtree_node_t*sentinel)
{
ngx_rbtree_node_t **p;
ngx_string_node_t *strNodeX;
ngx_string_node_t *strNodeY;
for ( ;; )
{
if(node->key != temp->key)
{
p = (node->key < temp->key) ?&temp->left : &temp->right;
}
else
{
//strNodeY =(ngx_string_node_t*)temp;
//或者这样用
strNodeY =ngx_rbtree_data(temp, ngx_string_node_t, node);
if(strNodeX->str.len != strNodeY->str.len)
{
p =(strNodeX->str.len < strNodeY->str.len) ? &temp->left :&temp->right;
}
else
{
p =(ngx_memcmp(strNodeX->str.data, strNodeY->str.data, strNodeX->str.len)< 0) ? &temp->left : &temp->right;
}
}
if (*p ==sentinel)
{
break;
}
temp = *p;
}
//每一个待插入的节点必须初始化为红色
*p = node;
node->parent =temp;//初始化父节点
node->left =sentinel;//初始化左子节点
node->right =sentinel;//初始化右子节点
ngx_rbt_red(node);//颜色设置为红色
}
void
travel_rbtree(ngx_rbtree_node_t* root, ngx_rbtree_node_t*sentinel)
{
if (root->left!= sentinel)
{
travel_rbtree(root->left,sentinel);
}
ngx_string_node_t*strNode = (ngx_string_node_t*)root;
printf("key= %d , str=%s\n", root->key, strNode->str.data);
if(root->right != sentinel)
{
travel_rbtree(root->right,sentinel);
}
}
nginx例子
nginx对静态文件cache的处理
Nginx中对静态文件进行了Cache,所有的cache对象包含在两个数据结构里面,整个机制最关键的也是这两个东西,一个是红黑树,一个是一个队列,其中红黑树是为了方便查找(需要根
据文件名迅速得到fd),而队列为了方便超时管理(按照读取顺序插入,在头的就是最近存取的文件),由于所有的句柄的超时时间都是一样的,因此每次只需要判断最后几个元素就够了,因为这个超时不需要那么实时。
首先,这里,cache的红黑树的key是一个hash值,是文件名的crc校验码:
//计算hash
hash = ngx_crc32_long(name->data, name->len);
//根据名字查找对应的file对象。
file = ngx_open_file_lookup(cache, name, hash);
首先我们来看如果没有找到cache对象的情况,此时nginx会open 这个文件,然后保存对应的信息到cache中。
然后就是更新队列的部分,这里可以看到插入是每次都插入到队列的头,之所以插入到头,是为了超时操作更方便。
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- oldu.cn 版权所有 浙ICP备2024123271号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务