内核里的红黑树

那谁不是说了么,“内核是个各种程序的大宝库,因为这些程序出自地球上顶尖程序员”。

可能,你的工程中需要用到红黑树,你应该不会亲自去写,能写你也不会去写。到哪里去找个现成的代码勒?

这种类似的疑问也困扰过曾经年少无知的我,其实,内核这儿不就有一个么!

 

红黑树是平衡二叉树的一种,它有很好的性质,树中的结点都是有序的,而且因为它本身就是平衡的,所以查找也不会出现非常恶劣的情况,基于二叉树的操作的时间复杂度是 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;
}

  

随便一写,重要的不是程序实现本身,而是如何利用内核这个大宝库的点点滴滴。

posted @ 2011-09-18 13:53  郝壹贰叁  阅读(464)  评论(0编辑  收藏  举报