linux红黑树实现

Linux内核红黑树的算法都定义在linux-2.6.38.8/include/linux/rbtree.h和linux-2.6.38.8/lib/rbtree.c两个文件中。

    1、结构体 

  1. struct rb_node  
  2. {  
  3.     unsigned long  rb_parent_color;  
  4. #define RB_RED      0   
  5. #define RB_BLACK    1   
  6.     struct rb_node *rb_right;  
  7.     struct rb_node *rb_left;  
  8. } __attribute__((aligned(sizeof(long))));  
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))));

     这里的巧妙之处是使用成员rb_parent_color同时存储两种数据,一是其双亲结点的地址,另一是此结点的着色。__attribute__((aligned(sizeof(long))))属性保证了红黑树中的每个结点的首地址都是32位对齐的(在32位机上),也就是说每个结点首地址的bit[1]和bit[0]都是0,因此就可以使用bit[0]来存储结点的颜色属性而不干扰到其双亲结点首地址的存储。

PS: ptmalloc 代码中也用到这些特性,通过将一结构体进行地址对齐后,该结构体的首元素就可以节省出几个位用作他用。这种方法可以更高效地利用内存空间。

PS:为什么在rb_node中需要保存其父亲节点的首地址啊?因为内核中如果进行树的遍历,不可能用递归实现(递归容易造成栈溢出)。这样做,可以更好地转化为迭代来进行更好的树的遍历。

操作rb_parent_color的函数: 

  1. #define rb_parent(r)   ((struct rb_node *)((r)->rb_parent_color & ~3))  //获得其双亲结点的首地址  
  2. #define rb_color(r)   ((r)->rb_parent_color & 1) //获得颜色属性  
  3. #define rb_is_red(r)   (!rb_color(r))   //判断颜色属性是否为红  
  4. #define rb_is_black(r) rb_color(r) //判断颜色属性是否为黑  
  5. #define rb_set_red(r)  do { (r)->rb_parent_color &= ~1; } while (0)  //设置红色属性  
  6. #define rb_set_black(r)  do { (r)->rb_parent_color |= 1; } while (0) //设置黑色属性  
  7.   
  8. static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)  //设置其双亲结点首地址的函数  
  9. {  
  10.     rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p;  
  11. }  
  12. static inline void rb_set_color(struct rb_node *rb, int color) //设置结点颜色属性的函数  
  13. {  
  14.     rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;  
  15. }  

红黑树的遍历:

4、遍历

    rb_first和rb_next函数可组成中序遍历,即以升序遍历红黑树中的所有结点。 

  1. struct rb_node *rb_first(const struct rb_root *root)  
  2. {  
  3.     struct rb_node  *n;  
  4.   
  5.     n = root->rb_node;  
  6.     if (!n)  
  7.         return NULL;  
  8.     while (n->rb_left)  
  9.         n = n->rb_left;  
  10.     return n;  
  11. }  
  12.   
  13. struct rb_node *rb_next(const struct rb_node *node)  
  14. {  
  15.     struct rb_node *parent;  
  16.   
  17.     if (rb_parent(node) == node)  
  18.         return NULL;  
  19.   
  20.     /* If we have a right-hand child, go down and then left as far 
  21.        as we can. */  
  22.     if (node->rb_right) {  
  23.         node = node->rb_right;   
  24.         while (node->rb_left)  
  25.             node=node->rb_left;  
  26.         return (struct rb_node *)node;  
  27.     }  
  28.   
  29.     /* No right-hand children.  Everything down and left is 
  30.        smaller than us, so any 'next' node must be in the general 
  31.        direction of our parent. Go up the tree; any time the 
  32.        ancestor is a right-hand child of its parent, keep going 
  33.        up. First time it's a left-hand child of its parent, said 
  34.        parent is our 'next' node. */  
  35.     while ((parent = rb_parent(node)) && node == parent->rb_right)  
  36.         node = parent;  
  37.   
  38.     return parent;  
  39. }  

一个遍历的演示程序:

  1. void print_rbtree(struct rb_root *tree)  
  2. {  
  3.     struct rb_node *node;  
  4.       
  5.     for (node = rb_first(tree); node; node = rb_next(node))  
  6.     printf("%d ", rb_entry(node, struct mytype, my_node)->num);  
  7.       
  8.     printf("\n");  
  9. }  

PS:linux内核中将这种遍历做成了STL迭代器的形式,从而更好地进行代码的复用。

PS:感觉linux 内核中还有些 for_each宏之类的东西,是想做成闭包的形式,也是模仿STL的思想,更好地进行代码复用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值