内核里的红黑树
那谁不是说了么,“内核是个各种程序的大宝库,因为这些程序出自地球上顶尖程序员”。
可能,你的工程中需要用到红黑树,你应该不会亲自去写,能写你也不会去写。到哪里去找个现成的代码勒?
这种类似的疑问也困扰过曾经年少无知的我,其实,内核这儿不就有一个么!
红黑树是平衡二叉树的一种,它有很好的性质,树中的结点都是有序的,而且因为它本身就是平衡的,所以查找也不会出现非常恶劣的情况,基于二叉树的操作的时间复杂度是 O(log(N))。
红黑树的规则:
1. 每个结点要么是红色要么是黑色;
2. 根结点必须是黑色;
3. 红结点如果有孩子,其孩子必须都是黑色;(红结点的父亲必是黑))
4. 从根结点到叶子的每条路径必须包含相同数目的黑结点。
这四条规则可以限制一棵排序树是平衡的。
struct rb_node
{
unsigned long rb_parent_color; //由于地址对齐,利用低两位必定为零的特点表示红或黑
#define RB_RED 0
#define RB_BLACK 1
struct rb_node *rb_right;
struct rb_node *rb_left;
} __attribute__((aligned(sizeof(long))));
结点的简单操作举例如下:
/* 获得地址 */
#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3))
/* 获得颜色 */
#define rb_color(r) ((r)->rb_parent_color & 1)
/* 测试颜色, 真为1 */
#define rb_is_red(r) (!rb_color(r))
#define rb_is_black(r) rb_color(r)
struct rb_root //这样做的好处是看起来不用传递二级指针了
{
struct rb_node *rb_node;
};
重点集中在lib/rbtree.c上,看看一些和红黑树相关的重要算法:
基本操作便是左旋和右旋:
staticvoid __rb_rotate_left(struct rb_node *node, struct rb_root *root)
{
struct rb_node *right = node->rb_right;
struct rb_node *parent = rb_parent(node);
if ((node->rb_right = right->rb_left))
rb_set_parent(right->rb_left, node);
right->rb_left = node;
rb_set_parent(right, parent);
if (parent)
{
if (node == parent->rb_left)
parent->rb_left = right;
else
parent->rb_right = right;
}
else
root->rb_node = right;
rb_set_parent(node, right);
}
一个基本的左旋实现,简单明了。再加上右旋函数,它们为下面的插入等服务。
/* 新插入的结点都设为叶子,染成红色,
* 插入后如果破坏了规则,通过调整颜色和旋转可以恢复,
* 二叉树又重新平衡。
*/
void rb_insert_color(struct rb_node *node, struct rb_root *root)
{
struct rb_node *parent, *gparent;
while ((parent = rb_parent(node)) && rb_is_red(parent))
{
gparent = rb_parent(parent);
if (parent == gparent->rb_left)
{
{
register struct rb_node *uncle = gparent->rb_right;
if (uncle && rb_is_red(uncle))
{
rb_set_black(uncle);
rb_set_black(parent);
rb_set_red(gparent);
node = gparent;
continue;
}
}
if (parent->rb_right == node)
{
register struct rb_node *tmp;
__rb_rotate_left(parent, root);
tmp = parent;
parent = node;
node = tmp;
}
rb_set_black(parent);
rb_set_red(gparent);
__rb_rotate_right(gparent, root);
} else {
{
register struct rb_node *uncle = gparent->rb_left;
if (uncle && rb_is_red(uncle))
{
rb_set_black(uncle);
rb_set_black(parent);
rb_set_red(gparent);
node = gparent;
continue;
}
}
if (parent->rb_left == node)
{
register struct rb_node *tmp;
__rb_rotate_right(parent, root);
tmp = parent;
parent = node;
node = tmp;
}
rb_set_black(parent);
rb_set_red(gparent);
__rb_rotate_left(gparent, root);
}
}
rb_set_black(root->rb_node);
}
对于删除操作它要先执行像普通二叉查找树的“删除”,
然后根据删除结点的颜色来判断是否执行进一步的操作。
删除的接口是:
void rb_erase(struct rb_node *node, struct rb_root *root);
其实它并没有真正删除node,而只是让它和以root为根的树脱离关系,
最后它还要判断是否调用__rb_erase_color来调整。
其余的几个接口就比较简单了。
在以root为根的树中找出并返回最小的那个结点,只要从根结点一直向左走就是了。
struct rb_node *rb_first(struct rb_root *root);
找出并返回最大的那个,一直向右走
struct rb_node *rb_last(struct rb_root *root);
返回node在树中的后继。
如果node的右孩子不为空,它只要返回node的右子树中最小的结点即可;
如果为空,它要向上查找,找到迭带结点是其父亲的左孩子的结点,返回父结点。如果一直上述到了根结点,返回NULL。
struct rb_node *rb_next(struct rb_node *node);
返回node的前驱。
struct rb_node *rb_prev(struct rb_node *node);
用new替换以root为根的树中的victim结点
void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root);
红黑树接口使用的一个典型例子如下:
/*
Some example of insert and search follows here. The search is a plain
normal search over an ordered tree. The insert instead must be implemented
int two steps: as first thing the code must insert the element in
order as a red leaf in the tree, then the support library function
rb_insert_color() must be called. Such function will do the
not trivial work to rebalance the rbtree if necessary.
*/
-----------------------------------------------------------------------
static inline struct page * rb_search_page_cache(struct inode * inode,
unsigned long offset)
{
struct rb_node * n = inode->i_rb_page_cache.rb_node;
struct page * page;
while (n)
{
page = rb_entry(n, struct page, rb_page_cache);
if (offset < page->offset)
n = n->rb_left;
elseif (offset > page->offset)
n = n->rb_right;
else
return page;
}
return NULL;
}
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;
}
static inline struct page * __rb_insert_page_cache(struct inode * inode,
unsigned long offset,
struct rb_node * node)
{
struct rb_node ** p =&inode->i_rb_page_cache.rb_node;
struct rb_node * parent = NULL;
struct page * page;
while (*p)
{
parent =*p;
page = rb_entry(parent, struct page, rb_page_cache);
if (offset < page->offset)
p =&(*p)->rb_left;
elseif (offset > page->offset)
p =&(*p)->rb_right;
else
return page;
}
rb_link_node(node, parent, p);
return NULL;
}
static inline struct page * rb_insert_page_cache(struct inode * inode,
unsigned long offset,
struct rb_node * node)
{
struct page * ret;
if ((ret = __rb_insert_page_cache(inode, offset, node)))
gotoout;
rb_insert_color(node, &inode->i_rb_page_cache);
out:
return ret;
}
随便一写,重要的不是程序实现本身,而是如何利用内核这个大宝库的点点滴滴。