红黑树是一种有序的平衡二叉树,5个性质:
1、 每个结点的颜色只能是红色或黑色。
2、 根结点是黑色的。
3、 每个叶子结点都带有两个空的黑色结点(被称为黑哨兵),如果一个结点n只有一个左孩子,那么n的右孩子是一个黑哨兵;如果结点n只有一个右孩子,那么n的左孩子是一个黑哨兵。
4、 如果一个结点是红的,则它的两个儿子都是黑的。也就是说在一条路径上不能出现相邻的两个红色结点。果然富不过二代,如果一个结点是黑的,那它是可以有黑儿子的。
5、 对于每个结点来说,从该结点到其子孙叶结点的所有路径上包含相同数目的黑结点。
性质4和5保证了,在一棵二叉查找树上,执行查找、插入、删除等操作的时间复杂度为O(lgn)。
红黑树在Linux内核中的应用
一 主要数据结构
1 rb_node
struct rb_node {
unsigned long __rb_parent_color;
struct rb_node *rb_right;
struct rb_node *rb_left;
} __attribute__((aligned(sizeof(long))));
/* The alignment might seem pointless, but allegedly CRIS needs it */
struct rb_root {
struct rb_node *rb_node;
};
#define RB_RED 0
#define RB_BLACK 1
几个相关的宏定义:
//取出parent的指针,将__rb_parent_color的低2bit清0
#define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3))
//取出颜色
#define __rb_color(pc) ((pc) & 1)
#define rb_color(rb) __rb_color((rb)->__rb_parent_color)
2 rb_augment_callbacks
struct rb_augment_callbacks {
void (*propagate)(struct rb_node *node, struct rb_node *stop);
void (*copy)(struct rb_node *old, struct rb_node *new);
void (*rotate)(struct rb_node *old, struct rb_node *new);
};
二 主要函数
2 rb_first(),已知树根,查找first node最小或最大的结点,就是最左边的叶子结点。
3 rb_last(),已知树根,查找last node最大或最小,就是最右边的叶子结点。
4 rb_next(),已知一个结点,查找next node后继结点,这要分几种情况:
(1) 一棵空树,只有一个结点,自己是自己的parent,直接return NULL。
(2) 右子结点不为NULL,先找到它的右结点,然后在右子树中一路左下去;红黑树的有序,是中序遍历;所以next要一路左下去。
(3) 假设树是从小到大的排列顺序,没有右孩子,就要找到自己的父亲;如果自己是个右孩子,那自己一直是最大的;只有自己为左孩子的时候,其父亲和右哥哥才是比自己大的;按照这个原理,一直找。如果找到自己是个左孩子了,父亲就是next了;如果找到根了,根的父亲是NULL啊,最后return parent就是个NULL。
5 rb_prev(),已知一个结点,查找prev node,就是先找到它的左结点,然后一路右下去。
6 rb_replace_node(),已知树根,用new node代替victim node。
7 rb_link_node(),插入一个node。
static inline void rb_link_node(struct rb_node * node, struct rb_node * parent,
struct rb_node ** rb_link)
{
node->__rb_parent_color = (unsigned long)parent;
node->rb_left = node->rb_right = NULL;
*rb_link = node;
}
8 rb_insert_color(),插入后的平衡调整。
void rb_insert_color(struct rb_node *node, struct rb_root *root)
{
__rb_insert(node, root, dummy_rotate);
}
9 rb_erase(),删除一个node。
void rb_erase(struct rb_node *node, struct rb_root *root)
{
struct rb_node *rebalance;
rebalance = __rb_erase_augmented(node, root, &dummy_callbacks);
if (rebalance)
____rb_erase_color(rebalance, root, dummy_rotate);
}
三 应用实例
struct timerqueue_node {
struct rb_node node;
ktime_t expires;
};
struct timerqueue_head {
struct rb_root head;
struct timerqueue_node *next;
};
1 初始化树根root
static inline void timerqueue_init_head(struct timerqueue_head *head)
{
head->head = RB_ROOT;
head->next = NULL;
}
#define RB_ROOT (struct rb_root) { NULL, }
2 初始化结点
static inline void timerqueue_init(struct timerqueue_node *node)
{
RB_CLEAR_NODE(&node->node);
}
#define RB_CLEAR_NODE(node) \
((node)->__rb_parent_color = (unsigned long)(node))
3 添加一个结点
void timerqueue_add(struct timerqueue_head *head, struct timerqueue_node *node)
{
struct rb_node **p = &head->head.rb_node;
struct rb_node *parent = NULL;
struct timerqueue_node *ptr;
/* Make sure we don't add nodes that are already added */
WARN_ON_ONCE(!RB_EMPTY_NODE(&node->node));
while (*p) {
parent = *p;
ptr = rb_entry(parent, struct timerqueue_node, node);
if (node->expires.tv64 < ptr->expires.tv64)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&node->node, parent, p);
rb_insert_color(&node->node, &head->head);
if (!head->next || node->expires.tv64 < head->next->expires.tv64)
head->next = node;
}
(1) 如果node是刚刚初始化没有add过的,!RB_EMPTY_NODE(&node->node)为0;就不会有警告输出了。
(2) 找到爸爸,插入结点。由于hrtimer要求按照到期时间从小到大的顺序排序,所以我们从根root开始找,如果node的到期时间小,就在左子树中继续;如果node的到期时间大,就在右子树中继续;循环迭代,直到找到一个叶子结点结束,此时parent已经找到,它是一个叶子结点。
(3) rb_link_node()把node插入到树中,貌似插入的都是叶子结点。为什么这样就是插入了呢?传入的参数p中存的是2中找到的parent的一个孩子的二级指针,parent是个叶子结点,它的孩子基本就是个黑哨兵了,rb_link_node()中会*rb_link = node,这是让parent不再指向黑哨兵,而是指向node。
(4) rb_insert_color()是插入后做平衡调整。
(5) head->next = node;是因为hrtimer会用timerqueue_head->next存储最快到期的timerqueue_node,所以需要根据到期时间更新next。
4 删除一个结点
void timerqueue_del(struct timerqueue_head *head, struct timerqueue_node *node)
{
WARN_ON_ONCE(RB_EMPTY_NODE(&node->node));
/* update next pointer */
if (head->next == node) {
struct rb_node *rbn = rb_next(&node->node);
head->next = rbn ?
rb_entry(rbn, struct timerqueue_node, node) : NULL;
}
rb_erase(&node->node, &head->head);
RB_CLEAR_NODE(&node->node);
}
(1) 是不是空树,是就给个警告。
(2) timerqueue_head->next存的是最快到期的timerqueue_node,如果需要删除的就是它,那需要更新一下next。找到node的下一个,自然成为最后到期的timerqueue_node。
(3) 用rb_erase()删除,其实就是把它从红黑树中脱离;然后进行平衡调整。
(4) 初始化被删除的结点,就是把自己赋给自己的爸爸。
struct timerqueue_node *timerqueue_iterate_next(struct timerqueue_node *node)
{
struct rb_node *next;
if (!node)
return NULL;
next = rb_next(&node->node);
if (!next)
return NULL;
return container_of(next, struct timerqueue_node, node);
}
利用rb_next()找,分前面说过的三种情况。